standish sol manual ece223

258
Table of Contents Solutions for Chapter 1 — Preparing for the Journey Problems and Exercises for Chapter 1, p. 1 Solutions for Chapter 2 — Linked Data Representations Section 2.3 p. 2 Section 2.4 p. 3 Section 2.5 p. 4 Section 2.6 p. 7 Solutions for Chapter 3 — Introduction to Recursion Section 3.2 p. 8 Section 3.3 p. 13 Section 3.4 p. 14 Solutions for Chapter 4 — Modularity and Data Abstraction Section 4.2 p. 15 Section 4.3 p. 15 Section 4.4 p. 16 Section 4.5 p. 18 Section 4.6 p. 19 Solutions for Chapter 5 — Introduction to Software Engineering Concepts Section 5.2 p. 21 Section 5.3 p. 26 Section 5.4 p. 29 Section 5.5 p. 30 Section 5.6 p. 32 Section 5.7 p. 32 Section 5.8 p. 33 Solutions for Chapter 6 — Introduction to Analysis of Algorithms Section 6.2 p. 36 Section 6.3 p. 37 Section 6.4 p. 38 Section 6.5 p. 40 Section 6.6 p. 46 Solutions for Chapter 7 — Linear Data Structures — Stacks and Queues Section 7.2 p. 47 Section 7.3 p. 48 Section 7.4 p. 49

Upload: jaxon

Post on 17-Feb-2016

237 views

Category:

Documents


6 download

DESCRIPTION

Standish Sol Manual Ece223

TRANSCRIPT

Page 1: Standish Sol Manual Ece223

Table of Contents

Solutions for Chapter 1 — Preparing for the JourneyProblems and Exercises for Chapter 1, p. 1

Solutions for Chapter 2 — Linked Data RepresentationsSection 2.3 p. 2Section 2.4 p. 3Section 2.5 p. 4Section 2.6 p. 7

Solutions for Chapter 3 — Introduction to RecursionSection 3.2 p. 8Section 3.3 p. 13Section 3.4 p. 14

Solutions for Chapter 4 — Modularity and Data AbstractionSection 4.2 p. 15Section 4.3 p. 15Section 4.4 p. 16Section 4.5 p. 18Section 4.6 p. 19

Solutions for Chapter 5 — Introduction to Software EngineeringConcepts

Section 5.2 p. 21Section 5.3 p. 26Section 5.4 p. 29Section 5.5 p. 30Section 5.6 p. 32Section 5.7 p. 32Section 5.8 p. 33

Solutions for Chapter 6 — Introduction to Analysis of AlgorithmsSection 6.2 p. 36Section 6.3 p. 37Section 6.4 p. 38Section 6.5 p. 40Section 6.6 p. 46

Solutions for Chapter 7 — Linear Data Structures — Stacks andQueues

Section 7.2 p. 47Section 7.3 p. 48Section 7.4 p. 49

Page 2: Standish Sol Manual Ece223

Table of Contents — continued

Section 7.5 p. 50Section 7.6 p. 53Section 7.7 p. 56Section 7.8 p. 56Section 7.9 p. 59

— i —

Solutions for Chapter 8 — Lists, Strings, and Dynamic MemoryAllocation

Section 8.2 p. 60Section 8.3 p. 67Section 8.4 p. 68Section 8.5 p. 69Section 8.6 p. 71

Solutions for Chapter 9 — TreesSection 9.2 p. 74Section 9.3 p. 74Section 9.4 p. 75Section 9.5 p. 75Section 9.6 p. 78Section 9.7 p. 83Section 9.8 p. 91Section 9.9 p. 105Section 9.10 p. 107Section 9.11 p. 107

Solutions for Chapter 10 — GraphsSection 10.2 p. 112Section 10.3 p. 112Section 10.4 p. 117Section 10.5 p. 122Section 10.6 p. 128Section 10.7 p. 134Section 10.8 p. 142

Solutions for Chapter 11 — Hashing and the Table ADTSection 11.2 p. 145Section 11.3 p. 146Section 11.4 p. 150Section 11.5 p. 152Section 11.6 p. 157Section 11.7 p. 159

Solutions for Chapter 12 — External Collections of DataSection 12.2 p. 160

Page 3: Standish Sol Manual Ece223

Table of Contents — continued

Section 12.3 p. 161Section 12.4 p. 162Section 12.5 p. 163

Solutions for Chapter 13 — SortingSection 13.2 p. 164Section 13.3 p. 164Section 13.4 p. 166Section 13.5 p. 169Section 13.6 p. 170Section 13.7 p. 179Section 13.8 p. 182

— ii —

Solutions for Chapter 14 — Advanced RecursionSection 14.2 p. 183Section 14.3 p. 184Section 14.4 p. 190Section 14.5 p. 197

Solutions for Chapter 15 — Object-Oriented ProgrammingSection 15.2 p. 202Section 15.3 p. 211Section 15.4 p. 213

Solutions for Chapter 16 — Advanced Software EngineeringConcepts

Section 16.2 p. 215Section 16.3 p. 217Section 16.4 p. 218

Solutions for the Math Reference and Tutorial AppendixSection A.1 p. 220Section A.2 p. 220Section A.3 p. 221Section A.4 p. 221Section A.5 p. 222Section A.6 p. 223Section A.7 p. 224Section A.8 p. 226

Page 4: Standish Sol Manual Ece223

Table of Contents — continued

— iii —

Page 5: Standish Sol Manual Ece223

Table of Contents — continued

Solutions to Problems and Exercises in Chapter 1

1. If the array A contains no negative integers, A [ M a x I n d e x ] willeventually be evaluated in the while-condition of the while-loop. Ifarray index bounds checking is turned on, the erroneous arrayaccess A[MaxIndex], in which MaxIndex is out of bounds of the indexrange 0:MaxIndex–1 of A, will be detected. The error can be fixed byreplacing line 5 with

5 | while ( (i < MaxIndex) && (A[i] >= 0) ) {

This works because in the “short-circuit and expression” A && B, thesubexpression A is evaluated before evaluating the subexpressionB , and if A ’s value is false (i.e., 0), then the value of the entireexpression A && B is taken to be false without evaluating B. Hence,when i’s value is increased so that i == MaxIndex, the subexpression (i

< MaxIndex) evaluates to false and the erroneous out-of-rangeaccess of A[MaxIndex] in (A[i] >= 0) is never performed.

2. The statement of the problem on page 16 states that, “In order toavoid doing useless work, the solution should exit as soon as thefirst negative integer is discovered.” The proposed solution doesnot exit as soon as the first negative integer is discovered.Rather, it examines all array items in descending order and savesthe index of the most recent negative integer encountered indescending order. The conditions of the problem statement aretherefore not satisfied.

3. Hardware tends to come and go rather rapidly. Vendors releasenew models of computers, usually with increased performance orlower cost, at frequent intervals in order to keep up with thecompetition. Programming languages and software tend to rideout the rapid shifts in underlying computers by being rehosted onnew platforms — although software versions go through rapidevolution, too, in response to competitive market forces. Thefundamental laws of computing seem to have the greatestlongevity. For example, the discovery of the n  log n barrier forcomparison-based sorting has endured since its discovery.Various human-computer interfaces undergo evolution as thedecades pass. The early paper-tape and punched card inputs andelectric typewriter or teletype outputs gave way to cathode raytube screens with lines of characters, and these, in turn, gave wayto windows, mice, and the “desktop metaphor.” If versatile,

Page 6: Standish Sol Manual Ece223

Table of Contents — continued

efficient voice input were to evolve, it might change the nature ofthe human-computer interface even further.

Page 7: Standish Sol Manual Ece223

Table of Contents — continued

Answers to Review Questions 2.31. When we dereference a pointer, we follow the pointer to the unit

of storage for which the pointer value is the address (in someaddressable storage medium in which units of storage have uniqueaddresses that identify them). If A is a pointer variable in C, thenotation *A dereferences the pointer value in A.

2. We can say that: B l inks to A , or that A is B ’s referent, or that B

references A.

3. typedef int *IntegerPointer;

4. Execute the assignment A = (T*) malloc(sizeof(T)), after which A containsa pointer to a new block of storage of type T.

5. *A = 19 assigns 19 to be the value of A ’s referent. The expression2*(*A) has a value equal to twice the value of A’s referent.

6. It is a type mismatch, since A must take pointer values, and since 5is not a pointer value, but rather is an integer value.

7. Two different expressions that reference the same unit of storageare called aliases.

8. You execute the function call free(P), where P contains a pointer tothe storage to be recycled.

9. Dynamically allocated storage becomes inaccessible when thereare no pointers to it that can be reached either directly as valuesof pointer variables, or indirectly by following links in datastructures along a path that can reach it.

10. Garbage is inaccessible dynamically allocated storage that is nolonger needed during the execution of a program.

11. A dangling pointer is a pointer to a unit of storage that has beenreturned to the pool of unallocated dynamic storage.

12. The scope of a variable in C relates to the lifetime of its existenceand the places it is visible. During the time a variable exists and isvisible (by virtue of its name not being hidden by another variableof the same name that has a more local scope), the value stored inthe storage location associated with the variable can be bothassigned and accessed, using the variable’s name. The scope of aunit of dynamic storage allocated in C can be thought of as globalto the scope of named variables in a C program. This means thatthe lifetime of a unit of dynamically allocated storage lasts fromthe moment the unit is allocated (using malloc) until the moment theunit’s storage is reclaimed (using free(P)). So long as a pointer to aunit of storage exists, and so long as that unit of storage has not

Page 8: Standish Sol Manual Ece223

Table of Contents — continued

been reclaimed, the value in the unit can be accessed and a newvalue can be assigned to it.

13. Since units of dynamically allocated storage do not have names ina C program, they are sometimes called anonymous variables,meaning variables with no names. Names for variables are createdin the text of a C program, but, since dynamically allocatedstorage is created at the time a program is executed, rather thanwhen the program is written, there is no way to give textual namesto units of dynamically allocated storage. However, the pointer toa unit of dynamically allocated storage acts in place of the name ofan ordinary variable, since, using the pointer, the value stored inthe storage unit can be both assigned and accessed.

Solutions to Exercises 2.31. It prints 7.

2. It prints 5.

3. Answer depends on the behavior of your C system and isdetermined by experiment.

4. Answer depends on the behavior of your C system and isdetermined by experiment.

Answers to Review Questions 2.41. The null address is a special address that is not the address of any

node, and which, by convention, is used to indicate the end oflinked lists.

2. By a dot ( • ) or by a dot connected to an arrow pointing to thevalue NULL.

3. The value NULL represents the null address in C.

4. By a solid dot ( • ) in a link field.

5. An empty linked list is a list L having no nodes on it. By convention,it is indicated by the value NULL.

6. Explicit pointer variable notation consists of a box containing thetail of an arrow representing the pointer value, where the box islabeled on the left with the name of the pointer variable followedby a colon. Implicit pointer variable notation consists of an ovalcontaining the pointer variable name in which an arrow connectsthe boundary of the oval to the referent of the pointer value. Thetwo notations are equivalent. The implicit pointer variablenotation is used in most diagrams in the book.

Page 9: Standish Sol Manual Ece223

Table of Contents — continued

7. By a question mark (?), or, in the case of a pointer to an unknownlocation, by an arrow pointing to a circle containing a questionmark.

Solutions to Exercises 2.41. (a) NULL, (b) GCM, (c) MEX, (d) NULL.

2. (a) L–>Link, (b) L–>Link–>Link, (c) L, (d) *L.

3. N–>Link = L–>Link–>Link; L–>Link–>Link = N;

4.

L MIA MEX

Airport AirportLink LinkAirport Link

ORD

Airport Link

GCMN NULL

5. strcpy(L–>Link–>Airport, "JFK");

6. N–>Link = L–>Link–>Link; free(L–>Link); L–>Link = N;

Answers to Review Questions 2.51. In top-down programming using stepwise refinement, one starts

with an outline of a program which leaves out specific details, andone progressively fills in more details by a process known asstepwise refinement. In stepwise refinement, at each step morespecific detail is filled in, until finally, a specific executableprogram has been created, written in an actual programminglanguage.

2. We can define a struct for a NodeType that is tagged with the nameNodeTag and we can use this tag to define a Link member of thestruct whose type is a pointer to the NodeType struct being defined,as shown in the type definition struct given at the beginning ofExercises 2.5 on page 53.

3. The value NULL belongs to every pointer type in C.

Solutions to Exercises 2.51.

Page 10: Standish Sol Manual Ece223

Table of Contents — continued

void InsertNewFirstNode(AirportCode A, NodeType **L){

NodeType *N;

/* Allocate a new node and let the pointer variable N point to it */N = (NodeType *) malloc(sizeof(NodeType));

/* Set the Airport field of N’s referent to A */strcpy(N–> Airport, A);

/* Change the Link field of N’s referent to point to the first node of list L */N–> Link = *L ;

/* Change L to point to the node that N points to */*L = N;

}

2.void DeleteFirst(NodeType **L){

NodeType *N;

if (*L != NULL) {N = *L;*L = (*L)–>Link;free(N);

}}

3.void InsertBefore(NodeType *N, NodeType *M){

AirportCode A;

/* insert node M after node N on list L */M–>Link = N–>Link;N–>Link = M;

/* swap airport codes in N and M */strcpy(A, N–>Airport);strcpy(N–>Airport, M–>Airport);strcpy(M–>Airport, A);

}

4.NodeType *Copy(NodeType *L){

NodeType *N, *M, *L2;

if (L == NULL) {

return NULL;

} else {

/* initialization and copying of first node */

M = (NodeType *) malloc(sizeof(NodeType));L2 = M;N = L;strcpy(M–>Airport, N–>Airport);

/* L2 points to the copy of the list L being constructed *//* N is a pointer that steps along the nodes of L to copy *//* M is a pointer that steps along the nodes of L2 that are copies */

Page 11: Standish Sol Manual Ece223

Table of Contents — continued

/* of the corresponding nodes in L that N points to */

while (N–>Link != NULL) {M–>Link = (NodeType *) malloc(sizeof(NodeType));N = N–>Link; /* create new last node on copy list */M = M–>Link; /* advance pointers on both lists */strcpy(M–>Airport, N–>Airport); /* copy airport code */

}

/* mark last node of copy as the end of the list L2 */M–>Link = NULL;

/* return pointer to first node of L2 as the function result */return L2;

}}

5.void Reverse(NodeType **L){

NodeType *R, *N;

R = NULL; /* initialize R, the reversed list, to be the empty list */

while (*L != NULL) {N = *L; /* let N point to L’s first node */*L = (*L)–>Link; /* now, let L point to the remainder of L */N–>Link = R; /* link N to the rest of R */R = N; /* and make R point to its new first node N */

}

*L = R; /* finally, replace L by a pointer to the reversed list R */

}

6. If L is the null list (meaning L = NULL), then, when the condition inthe while-loop is evaluated, the attempt to find the value of(strcmp(L–>Airport, A) != 0) will try to dereference the null pointer, NULL.This error can be fixed by reversing the order of the operands ofthe short-circuit & & operator in the while-condition on line 4giving: while ( (L != NULL) && (strcmp(L–>Airport, A) != 0) ) {

7.NodeType *Concat(NodeType *L1, NodeType *L2){

NodeType *N;

if (L1 == NULL) {return L2;

} else {N = L1; /* let N point to the first node of L1 */while (N–>Link != NULL) N = N–>Link; /* find the last node of L1 */N–>Link = L2; /* set the link of the last node of L1 to L2 */return L1; /* return the pointer to the concatenated lists */

}

}

Page 12: Standish Sol Manual Ece223

Table of Contents — continued

8. If L points to a list consisting of just one node, then the functioncall L a s t N o d e ( L ) results in executing a statement that tries todereference N U L L . This is another example of a bug found bychecking a boundary case.

9. Suppose L is a list containing only one node. That is, suppose L

contains a pointer to a node whose link is NULL. Then the specialcase on lines 9:12 of Program 2.15 applies, and we free the node *L

points to, after which we need to store a NULL pointer in thevariable L external to the function call. To do this, we need to useL’s external address, &L, as an actual parameter when the functionis called and we need to use *L = NULL inside the function in order tostore NULL in the location to which the actual parameter &L points.If the function prototype had been void DeleteLastNode(NodeType *L),there would be no way to set the contents of the external variableL to NULL in the case of a list consisting of only one node to bedeleted, since the actual parameter passed for the value of L atthe time of the function call would be the address of the first nodeon the list, leaving no way to change the contents of the externalvariable L after the deletion from a pointer to the first node of thelist to a NULL pointer.

Answers to Review Questions 2.61. Nodes having two separate pointer fields can be linked into two-

way lists, binary trees, and two-way rings.

2. In symmetrically linked lists, nodes point both to theirpredecessors and successors in the list — except for the firstnode in the list whose predecessor is NULL and the last node of thelist whose successor is NULL.

x2 SANORDBRU

AirportRightLink

LeftLinkAirport

RightLink

LeftLink Airport

RightLink

LeftLink

Solution to Exercise 2.61.

void Delete(NodeType *L){

/* Make L’s predecessor point to its successor */if (L–>LeftLink != NULL) {

L–>LeftLink–>RightLink= L–>RightLink;}

Page 13: Standish Sol Manual Ece223

Table of Contents — continued

/* Make L’s successor point to its predecessor */if (L–>RightLink != NULL) {

L–>RightLink–>LeftLink= L–>LeftLink;}

/* Dispose of the storage for node L */free(L);

}

Page 14: Standish Sol Manual Ece223

Table of Contents — continued

Answers to Review Questions 3.21. The base case occurs when a recursive program gives a direct

solution to a subproblem without using any recursive calls tocompute it.

2. Four decomposition methods: (i) First & Rest, where First = m andRest = m +1:n; (ii) Last & All but Last, where Last = n and All butLast = m :n–1, (iii) Split in Halves, where LeftHalf = m :midd le ,RightHalf = middle+1:n, and middle = (m + n) / 2; and (iv) Edges &Center, where Edges = (m & n) and Center = m+1:n–1.

3. A call tree is a tree with a recursive call at its root, in which thedescendants of each node show the recursive calls made by thecall at that node. Calls resulting in base cases have nodescendants, and are the so-called leaves of the call tree.

4. A trace of a recursive function call is a sequence of successivelines on which the calling expressions for successive recursivecalls are given amidst the values waiting to be combined with theresults returned by those recursive calls. When the base cases arereached and values are returned directly from them, the traceshows how the values combine to produce the value returned bythe original function call.

5. The number of different combinations of n things taken k at a time

is given by the formula:

  n  

  k         =        n!

k!(n–k)! .

6. Decompose a non-empty list into its Head (which is its first node)and its Tail (consisting of the rest of the nodes on the list afterthe first one).

Solutions to Selected Exercises in Exercises 3.21.

double Power(double x, int n){

if (n == 0) {return 1.0; /* base case, x^0 == 1.0 */

} else {return x * Power(x, n – 1); /* recursion step */

}}

2.double Power(double x, int n){

double p;

if (n == 0) {

return 1.0; /* base case, x**0 = 1.0 */

Page 15: Standish Sol Manual Ece223

Table of Contents — continued

} else {

p = Power(x, n / 2); /* let p == x to the half(n) power */

if ( (n % 2) == 0 ) {return p * p; /* if n was even, return Square(p) */

} else {return x * p * p; /* if n was odd, return x*Square(p) */

}

}

}

3.int Mult(int m, int n){

if (m == 1) {return n;

} else {return n + Mult(m – 1, n);

}}

4. Recursive Euclid’s Algorithm:

int gcd(int m, int n){

if (n == 0) {return m; /* base case, if remainder is 0, then result is m */

} else {return gcd(n, m % n) ; /* recursion step */

}}

5. First, to prove that gcd(m ,n) terminates, we know that when m isdivided by the divisor n to obtain a quotient q and a remainder r =m % n , the quantities obtained obey the relation: m = q *n + r,where 0 ≤ r < n . So, the remainder r is either 0 or a positiveinteger less than the divisor n . Thus, there can only be a finitenumber (less than n) of successive non-zero remainders, r, whichdecrease by at least 1 before a zero remainder is produced. Once azero remainder is obtained, the base case of the recursion (on line4 of gcd(m ,n)) is reached, and the function call terminates. Toprove that gcd(m,n) returns the greatest common divisor, we notethat on successive calls, if n = 0, then gcd(m ,n ) = m , andotherwise, gcd(m,n) = gcd(n,r). In the latter case, r is related to mand n by the equation r = m – q*n, so any common divisor of thepair (m,n) is also a common divisor of the pair (n,r) — including thegreatest common divisor. Consequently, the gcd of successiveremainder pairs is preserved. Finally, when n = 0, gcd(m,n) = m, so

Page 16: Standish Sol Manual Ece223

Table of Contents — continued

m is the last non-zero remainder in the sequence of remainderpairs, all of which were divisible by the gcd. This implies that mitself is the gcd.

6.int Product(int m, int n){

if (m < n) {return m * Product(m+1,n);

} else {return n;

}}

7. Despite what some conceive to be the elegance and simplicity ofthe R e v e r s e function given in the solution below, the overallsolution is a poor one on two counts: (1) The overall running time isquadratic instead of linear because of repeated scanning andcharacter copying of the successively smaller tails that arereversed, and (2) The scratch storage used to accommodateconcatentations and tails is taken from the dynamic storage poolusing malloc, and is never freed after use.

/****** Solution to Exercise 3.2.7 in Chapter 3, page 81.*****/

#include <stdio.h>#include <stdlib.h>#include <string.h>

char *EmptyString = "";

/* ------------------------------------------------------------------------------ */

int Empty(char *S){

return (S[0] == '\0');}

/* -------------------------------------------------------------- */

char *Head(char *S){

char *t;

t = (char *) malloc(2*sizeof(char));t[0] = S[0];t[1] = '\0';

return t;}

/* -------------------------------------------------------------- */

Page 17: Standish Sol Manual Ece223

Table of Contents — continued

char *Tail(char *S){

char *t, *temp;int n;

n = strlen(S);

if (n <= 1) {return EmptyString;

}else {t = (char *) malloc(n*sizeof(char));temp = t;S++;while ((*t++ = *S++) != '\0')

;}

return temp;}

/* -------------------------------------------------------------- */

char *Concat(char *S, char *T) /* see Program 8.17 on page 315 */{

char *P;char *temp;

P = (char *) malloc((1+strlen(S)+strlen(T))*sizeof(char));

temp = P;

while ((*P++ = *S++) != '\0');

P--;

while ((*P++ = *T++) != '\0');

return temp;}

/* -------------------------------------------------------------- */

char *Reverse(char *S) /* to Reverse a String S */{

char *temp;

if (Empty(S)) { /* the empty string is returned */return EmptyString; /* for the base case */

} else {return Concat(Reverse(Tail(S)), Head(S));

}}

/* ------------------------------------------------------------------------------ */

int main(void){

char *S = "abcdefg", *S1;

Page 18: Standish Sol Manual Ece223

Table of Contents — continued

printf("the string S == %s\n",S);S1 = Reverse(S);printf("the reverse of S == %s\n",S1);

}

/* ------------------------------------------------------------------------------ */

8. The answer to exercise 8 is not given.

9.int Length(NodeType *L) /* to compute the number of */{ /* nodes in a linked list L */

if (L == NULL) {return 0; /* since the empty list has no nodes in it */

} else {return 1 + Length(L–>Link); /* length = 1 + length of L’s tail */

}}

10.int Min2(int A[ ], int m, int n) /* first define an auxiliary function Min2 */{

int MinOfRest;

if (m == n) {return A[m];

} else {MinOfRest = Min2(A,m+1,n);if (A[m] < MinOfRest) {

return A[m];} else {

return MinOfRest;}

}}

int Min(int A[ ]) /* to find the smallest integer in an integer array A[0:n–1] */{

return Min2(A,0,n–1);}

11.int Ack(int m, int n) /* assume m ≥ 0 and n ≥ 0 */{

if (m == 0) {return (n+1);

} else if (n == 0) {return Ack(m–1,1);

} else {return Ack(m–1,Ack(m,n–1));

}}

12. The answer to exercise 12 depends on the behavior of your Csystem.

Page 19: Standish Sol Manual Ece223

Table of Contents — continued

13. The procedure, P(n), writes the digits of the non-negative integern.

14. The procedure, R(n), writes the digits of the integer n in reverseorder.

15. Using the auxiliary function NewtonSqrt(x,epsilon,a), call Sqrt(x,epsilon) inwhat follows (making sure to #include <math.h> beforehand):

double NewtonSqrt(double x, double epsilon, double a){

if (fabs(a*a – x) <= epsilon) {return a;

} else {return NewtonSqrt(x, epsilon, (a + x/a)/2.0);

}}

double Sqrt(double x, double epsilon){

return NewtonSqrt(x,epsilon,x/2.0);}

16.int C(int n, int k) /* where n and k are non-negative integers */{

if ((k == 0) | | (n == k)) {return 1;

}else {return C(n–1,k) + C(n–1,k–1);

}}

17. The answer to exercise 17 is not given.

Answers to Review Questions 3.31. An infinite regress occurs when a recursive program calls itself

endlessly.

2. Two programming errors that can cause infinite regresses are: (1)a recursive program with no base case, or (2) a recursive programwith a base case that never gets called.

3. An infinite regress causes an unending sequence of recursive calls.To evaluate each such call, the C run-time system allocates a newcall frame which it allocates in a run-time call-frame storageregion. Thus, the call-frame region will become exhausted whenthe run-time system attempts endlessly to allocate new callframes.

4. If single precision integers were used as the integerrepresentation of the parameter, n, in Program 3.12, then the callFactorial(0) would result in the recursive calls Factorial(0),

Page 20: Standish Sol Manual Ece223

Table of Contents — continued

Factorial(–1), Factorial(–2), and so on, being made. If, for example,single precision integers are represented by 16-bit, two’scomplement integers, then after descending to the value –32,768,the values cycle back to 32,767 and descend towards 0 again,eventually reaching the value 1. When Factorial(1) is called, thefunction terminates with the value 1. Then, all the values in therange –32768:32768 are multiplied together, giving the value 0.This scenario can happen only if there is enough memory space tohold all 65536 calls in the call-frame storage region. But manycontemporary computers will have enough memory to do this.

Solution to Exercise 3.31. The function call F(2) causes an infinite regress.

Answers to Review Questions 3.41. The complexity class that characterizes the recursive solution of

the Towers of Hanoi puzzle is called the exponential complexityclass.

2. The principal disadvantage of the exponential complexity class isthat the running time of the algorithms that fall in this classrequire very large running times for all but very small arguments.Such running times are so large that we cannot expect an answerfrom these algorithms given a reasonably sized input for a long,long time. Thus, we generally try to avoid using such exponentialalgorithms.

Solution to Exercise 3.41. Since there are 3.1536*107 seconds in one year, and the length of

the instruction sequence is L(n ) = 2n – 1, we need to find thelargest n such that

2n – 1 ≤ 3.1536*107.

The largest such n is 24 (which can be determined by finding thelargest n such that n ≤ log2(3.1536*107 + 1), where log2(3.1536*107

+ 1) = 24.91049639). Hence, a tower of at most 24 disks can bemoved in a year’s time.

Page 21: Standish Sol Manual Ece223

Table of Contents — continued

Answers to Review Questions 4.21. A C module M is a program consisting of two separate coordinated

text files, M . h , its interface file (or header file) and M . c , itsimplementation f i le (or source file), such that M provides acollection of related entities all of which work together to offer aset of capabilities or to provide a set of services or componentsthat can be used to help solve some class of problems.

2. The interface file M.h of a module M is a text which declaresentities that are visible to (and hence usable by) external users.These entities can include declarations of constants, typedefs,variables, and functions. Only the prototypes of functions aregiven (and in them, only the argument types, and not the argumentnames, are given).

3. The implementation file M.c of a module M is a program text whichdeclares local program entities that are private to the module(and cannot be seen or used by external users), and which alsogives the full declarations of functions whose prototypes aregiven in the interface header file, M.h , and which are visible toexternal users.

4. To use the services provided by a module M, you place an includedirective which includes the header file M.h at the beginning ofyour program, using the syntax #include "M.h". The effect is as if thedeclarations inside M.h had been substituted in your program atthe place the include directive is given. The module M is usuallycompiled separately. The extern declarations in M.h tell the linkerhow to link in externally defined functions from other modulescompiled separately.

Answers to Review Questions 4.31. A priority queue is a collection of prioritized items in which items

can be inserted in any order of ranking of their priorities, but areremoved in highest-to-lowest order of their priorities.

2. A priority queue’s items could be stored in arrays (or linked lists,or trees, or many other kinds of data structures acting ascontainers). Considering only arrays as containers, the items couldbe stored either in sorted or unsorted order (where sorting isdone with respect to the order implied by the items’ priorities). Ifan unsorted array is used, you add a new item at the end of thearray, and to remove an item, you scan the array to locate the

Page 22: Standish Sol Manual Ece223

Table of Contents — continued

item of highest priority, remove it, and then move the last iteminto the hole created by the removal of the highest priority item.For sorted arrays, to insert an item, you move all items of prioritygreater than the item one space to the right, and insert the iteminto the hole created. To remove an item you simply remove thelast item in the array.

3. Given the sketch in the answer to the previous question, theunsorted array representation has a more efficient insertionoperation while the sorted array has a more efficient removaloperation. The reason is that these two efficient operationsoperate on only one item, whereas insertion into a sorted arrayand removal from an unsorted array potentially require all itemsin the array either to be scanned or to be moved.

Answers to Exercises 4.3

1.| PQItem Remove(PriorityQueue *PQ)| {

80 | PQItem temp;| PQListNode *NodeToFree;|| if ( ! Empty(PQ)) { /* result undefined if PQ empty */| NodeToFree = PQ–>ItemList; /* otherwise, remove the */

85 | temp = NodeToFree–>NodeItem; /* highest priority item */| PQ–>ItemList = NodeToFree–>Link; /* from the front of the list */| PQ–>Count – –; /* decrease the item count */| free(NodeToFree); /* and free the space for the */| return (temp); /* node that was removed */

90 | }| }

2. The items in the ItemArray are stored in positions 0:Count – 1. Given anI temAr ray that currently contains Count items, its last item willtherefore be found in the Count – 1 position. Thus, to remove thelast item, we first decrement the Count member of the PriorityQueue

struct, using PQ–>Count – –, and then we move the last item at P Q –

>I temArray[PQ–>Count ] into the hole opened up by removal of themaximum priority item at the position PQ–>ItemArray[MaxIndex].

Answers to Review Questions 4.41. A program shell is a top-level program with “holes” that invokes

and uses plug-in modules and organizes the operations andservices these modules provide to define the topmost level ofoperation of the overall program.

Page 23: Standish Sol Manual Ece223

Table of Contents — continued

2. Using modules can help organize the work in a software project byproviding a clean way to break the work of the project intosubprojects associated with implementation of the separatemodules.

3. Using modules can help structure a software system design duringthe design phase, by first defining only the interfaces (or headerfiles) of the modules (and not their detailed implementations) sothat the modules fit together cleanly in the overall design. Theoverall design will be clean, in general, if it is composed from afew modules having simple, well-defined interactions.

Answers to Exercises 4.41. Tic-Tac-Toe Program Shell:

| /* the following is the text of a TicTacToe Program Shell */|| #include <stdio.h>| #include "TicTacToeUserInterface.h"

5 | #include "MoveCalculationModuleInterface.h"|| Move theMove; /* theMove contains current player's move*/| Board theBoard; /* theBoard gives the configuration of the game */|

10 | int main (void)| {|| InitializeAndDisplayTicTacToeBoard( );|

15 | do {|| GetAndProcessOneEvent( ); /* Gets and displays user's move */| /* if user clicks in a square */| /* on the board. */

20 | /* Otherwise, lets user select X or O */| /* to play, and let's user choose who */| /* plays first — the user or the machine */|| if (ItIsMachinesTurnToMove( ) {

25 | theMove = CalculateMove(theBoard); /* done by */| /* MoveCalculationModule*/| Display(theMove); /* done by */| /* TicTacToeUserInterfaceModule */| }

30 || if (GameIsOver( ) ) {| DisplayResults( ); /* X wins, O wins, or Draw */| AskIfUserWantsToPlayAgain( );| }

35 || } while ( ! UserWantsToQuit( ) );|| Shutdown( );|| }

Page 24: Standish Sol Manual Ece223

Table of Contents — continued

Interface file of a module to handle the Tic-Tac-Toe userinterface:

| /* the text of the file "TicTacToeUserInterface.h" */|| #include <stdio.h>|

5 | typedef enum { false, true } Boolean ;| typedef enum {X, O, Blank} Token;| typedef Token Board[3][3];| typedef struct {| int row;

10 | int column;| } Move;|| extern Boolean ItIsMachinesTurnToMove(void);| extern Boolean UserWantsToQuit(void);

15 | extern void InitializeAndDisplayTicTacToeBoard(void);| extern void GetAndProcessOneEvent(void);| extern void Display(Move);| extern Boolean GameIsOver(void); /* true if there was a win or draw */| extern void DisplayResults(void);

20 | extern void AskIfUserWantsToPlayAgain(void);| extern void Shutdown(void);|| /* end of file "TicTacToeUserInterface.h" */

Interface file of a module to calculate the machine’s move:

| /* the text for the file "MoveCalculationModuleInterface.h" */|| #include <stdio.h>| #include "TicTacToeUserInterface.h" /* included in order to import the */

5 | /* typedefs for Move and Board */| /* needed below */| extern Move CalculateMove(Board);|| /* end of file "MoveCalculationModuleInterface.h" */

Answers to Review Questions 4.51. An available space list is a linked list of unused list nodes that is

organized as a pool of available storage to allocate during theoperation of a list-processing program. It is generally linkedtogether during the initialization of the program. Freshly allocatednodes are removed from it, and freed nodes are returned to itduring the execution of the program. It thus provides the basis fora storage allocation and management policy for user-definedlinked lists.

2. Information hiding occurs when program entities are declared andused locally within a function or module, preventing them frombeing seen and/or used outside the function or module.

Page 25: Standish Sol Manual Ece223

Table of Contents — continued

3. Information hiding can promote ease of program modification byconfining the region of program text in which changes have to bemade to a local region of the total program text. When entities aredefined locally within a function, or privately inside a module in itsimplementation file, then changes in the local or private entitiescan be made without interfering with entities outside.

4. A representation-independent notation is a notation which doesnot have to be changed when its underlying data representation ischanged. For example, GetLink(N) is a representation-independentnotation for getting the Link of node N in a linked-list, whereas thethree expressions N – > L i n k , L i s tMemory [N ] . L i nk , and L ink [N ] arerepresentation-dependent notations (from which, by looking atthe notations, one can discover what the underlying datarepresentations are).

5. If one does not use a representation-independent notation, thenthe users of the module will have to use notations that depend onthe data representations chosen, in which case the datarepresentation isn’t hidden properly (since it can be discoveredby studying the notation of use outside the module in which therepresentation is supposed to be hidden).

6. Efficiency trades off against generality whenever making aprogram more general makes it more costly to execute, orwhenever making it more efficient entails making it less general.Using a representation-dependent notation makes a programmore efficient but less general than using a representation-independent notation that hides a specific representation-dependent data access notation behind a general function callnotation. The latter, though more general, is less efficient becauseit incurs the expense of making extra function calls (i.e., thoseproviding the representation-independent “wrapper,” as it issometimes called, which hides the representation-dependentnotation inside).

7. The representation-independent notation for operating on nodesof a linked list is less efficient than the particular kinds ofrepresentation-dependent notations because using it incurs theextra cost of transmitting procedure parameters and makingprocedure calls and returns — which are extra expenses notincurred by using the representation-dependent notations.

Answers to Selected Exercises in Exercises 4.56. Nothing is wrong with the program. It will work as it is. However,

note that it redeclares the variable, Avai l, to be a string variablelocal to the function main( ), when an identically named variable is

Page 26: Standish Sol Manual Ece223

Table of Contents — continued

used in the implementation section of the module, ParallelArrayLists.This is acceptable, since the variable, Ava i l , used inside theimplementation file "L is t Imp lementa t ion .c " for the Parallel ArrayRepresentation module of Program 4.23 does not interfere withany identically named locally defined variables inside the main( )

procedure.

Answers to Review Questions 4.61. Modules are also called un i ts or p a c k a g e s in other modern

languages such as Pascal and Ada.

2. Procedural abstraction occurs when one creates a procedure, P(a1,a2, ..., an), as a named unit of action. Then, one can use the actionrepresented by P, knowing only what P does and not how P isimplemented. Later, one can change how P is implemented withoutchanging every instance of P’s use (which could not have happenedif the instructions giving P’s action had been repeated every timeP was used). The use of procedural abstraction therefore promotesease of use and change.

3. Data abstraction occurs when one hides the representation of adata structure and the implementation details of the operationson it using a representation-independent notation. This enablesthe abstract data to be used knowing only what it does, not how itsdetails are implemented. As with procedural abstraction, so alsowith data abstraction, by separating the what from the how, bothease of use and ease of modification are promoted.

4. Encapsu la t ion consists of hiding program entities (or certainfeatures of externally visible program entities) inside aprotective wall, called the capsule boundary, so that they canneither be seen nor used outside the capsule.

5. In summary, using C modules properly can provide for: (a) separatecompilation (which can reduce compilation times since the wholeprogram doesn’t need to be compiled each time), (b) ease ofmodification, (c) ease of use of the services provided by themodule, (d) help with software system design, and (e) help inorganizing the work of a software project.

Page 27: Standish Sol Manual Ece223

Table of Contents — continued

Page 28: Standish Sol Manual Ece223

Table of Contents — continued

Answers to Review Questions 5.21. The method of starting with a top-level design in “outline form”

and progressively “filling in” details until an actual program isdeveloped is called top-down programming. The method ofbuilding general-purpose, reusable software components, andlater on, using these components in system constructionactivities, is called bottom-up programming.

2. Suppose that a system we are constructing uses many layers ofdata structures and algorithms including top-level abstract datatypes (ADTs). Suppose the mechanics of the higher layers areimplemented entirely in terms of ADTs in a representation-independent notation, free from the details of lower-levelrepresentational choices. Further, suppose we do not choose arepresentation for the abstract data structures and algorithms inthe higher layers until the very last stage of implementation.Before choosing, we might even compare several options foralternative, substitutable data representations based onefficiency considerations and on the constraints of the problem weare trying to solve. If we choose to program in this fashion, we saywe have postponed the choice of data representations andalgori thms, since we have delayed choosing them until the lastpossible moment.

3. When implementing a program strategy, the goals in the programstrategy can be achieved by supplying more specific programmingdetails. The process of filling-in more specific details in severalsuccessive steps is called stepwise refinement. The abstracthigher-level, less-detailed program strategies are re f ined bytransforming them into lower-level, more-specific programs.

Solutions to Selected Exercises in Exercise 5.21. A complete C program that refines Program Strategies 5.4 — 5.8,

using a linked row of structs to represent the table is given asfollows:

| #include <stdio.h>| #include <stdlib.h>||

5 | /* type definition section */|| typedef struct RowTag{ /* defines types given in Ex. 5.2.1 */| int Amount; /* on page 153 */| int Repetitions;

Page 29: Standish Sol Manual Ece223

Table of Contents — continued

10 | struct RowTag*Link;| } RowStruct;|| typedef RowStruct *RowStructPointer;|

15 | typedef int InputArray[6];|| /* declarations of variables */|| RowStructPointer Table = NULL;

20 || InputArray A;||| /* Program 5.5 */

25 || void GetSixInputs(InputArray A) /* Get six dollar amounts */| { /* and put them in InputArray A */| int i;|

30 | printf("Give six dollar amounts separated by spaces: ");|| for (i = 0; i < 6; ++i) {|| scanf("%d",&A[i]); /* read in six dollar amouts */

35 | }| }||| /* Refinement of Program Strategy 5.7 */

40 || void InsertAmount(int AmountToinsert)| {| RowStructPointer RowPointer;| int UpdateCompleted;

45 || RowPointer = Table;| UpdateCompleted = 0; /* UpdateCompleted is a flag variable */|| while ((RowPointer != NULL) && ( ! UpdateCompleted)) {

50 | if (RowPointer–>Amount != AmountToinsert) {| RowPointer = RowPointer–>Link;| } else {| (RowPointer–>Repetitions)++;| UpdateCompleted = 1; /* set UpdateCompleted to true */

55 | }| }|| if ( ! UpdateCompleted) {| RowPointer = (RowStruct *) malloc(sizeof(RowStruct));

60 | RowPointer–>Amount = AmountToinsert;| RowPointer–>Repetitions = 1;| RowPointer–>Link = Table;| Table = RowPointer;| }

65 | }||| /* Refinement of Program Strategy 5.6 */|

70 | void MakeTable(int A[])| {

Page 30: Standish Sol Manual Ece223

Table of Contents — continued

| int i;|| for (i = 0; i < 6; ++i) {

75 || InsertAmount(A[i]);|| }| }

80 ||| /* Refinement of Program Strategy 5.8 */|| void FindAndPrintWinner(void)

85 | { | int AmountWon;| RowStructPointer RowPointer;|| AmountWon = 0;

90 | RowPointer = Table;|| while (RowPointer != NULL) {| if ((RowPointer –>Repetitions >= 3) &&| (RowPointer –>Amount > AmountWon)) {

95 | AmountWon = RowPointer –>Amount;| } else {| RowPointer = RowPointer –>Link;| }| }

100 || printf("You Won $% d\n",AmountWon);| }|| /* Refinement of Program Strategy 5.4 */

105 || void FindWinningAmount(void)| {| GetSixInputs(A);| MakeTable(A);

110 | FindAndPrintWinner();| }|| int main(void)| {

115 | FindWinningAmount( );| }| /* end of main */

2. Data collected in a study by Sackman, Erikson, and Grant showedthere exists a large variance between programmers (of factors upto twenty) in various measures of programmer performance suchas the time needed to write a program, the time needed to debuga program, the time and space required for the program to run, andso forth. For given programmers, however, these measures tendedto cluster together, in the sense that if a programmer took moretime to write a program, s/he tended to take more time to debugit, it tended to contain more errors, and it tended to run less

Page 31: Standish Sol Manual Ece223

Table of Contents — continued

efficiently. And, to the contrary, programmers who wrote conciseprograms, tended to write and debug them quickly, and theprograms tended both to contain fewer errors and to run moreefficiently. Thus, van Snipe’s position may have merit when appliedto differences in program quality among small program modules.However, in large programming projects, the advantages ofseparating the system design into representational layers( including subst i tutabi l i ty of representat ions, ease ofunderstanding and maintenance, etc.) can be lost if one makes apremature representational choice and tries to compress portionsof the system into a shortened form to attain cleverness ofexpression. Moreover, if such concise forms rely on clever tricksthat are difficult to understand, it can impose costs onmaintenance programmers who must later understand how thesystem works in order to upgrade the system to do somethingnew and different. Nonetheless, there are clearly situations inwhich concise, clever solutions are efficient, relatively error-free, and easy to understand if properly documented. Careful useof such solutions can contribute to a system’s performanceadvantages without being a burden for maintenance programmers.

3. Assuming the amount X is of higher priority than the amount Y if X≥ Y in the implementation of priority queues by thePQImplementation.c, we can write:

| /*| * Priority Queue Lottery Solution to Exercise 5.2.3| */|

5 | #include <stdio.h>| #include <stdlib.h>| #include "PQInterface.h" /* see interface in Program 4.3 */|| /* type definitions */

10 || typedef PQItem InputArray[6];|| /* declarations of variables */|

15 | InputArray A;|| PriorityQueue PQ;||

20 | /* Program 5.5 */|| void GetSixInputs(InputArray A) /* Get six dollar amounts */| { /* and put them in array A */|

25 | int i;|| printf("Give six dollar amounts separated by spaces: ");|

Page 32: Standish Sol Manual Ece223

Table of Contents — continued

| for (i = 0; i < 6; ++i) {30 |

| scanf("%d",&A[i]); /* read in six dollar amouts */| }|| }

35 ||| /* Exercise 5.2.2, pp. 153–154 — van Snipe's Short Lottery Solution */|| /* assume the global InputAray A has been declared */

40 | /* and is used in these routines */|| int ThreeOrMore(int Amt)| { int j;| int k = 0;

45 || for (j = 0; j < 6; ++j) if (A[j] == Amt) k++;| return( k >= 3 );| }|

50 ||| /* Program for Exercise 5.2.3 on page 154 — PQ Lottery Solution */|| void PQLotterySolution(void)

55 | {| int i, AmountWon;|| Initialize(&PQ); /* Initialize the priority queue, PQ, */| /* to make it empty */

60 | GetSixInputs(A); /*Get six inputs from the user */| for (i=0; i<6; ++i) {| if ( ThreeOrMore(A[i]) ) Insert(A[i], &PQ);| }| AmountWon = ( Empty(&PQ) ? 0 : Remove(&PQ));

65 | printf("You win $ %d\n",AmountWon);|| }||

70 | /* main program to test results */|| int main(void)| {| /* test priority queue lottery solution */

75 | PQLotterySolution( );|| }| /* end of main */

4. The if-condition (( A[i] == A[i+1]) && (A[i+2] == A[i+2] )) can be replaced bythe simpler if-condition ( A[i] == A[i+2] ) in this case because we aretesting for the presence of a run of three consecutive identicalvalues in a sorted array whose values are given in increasingorder. Here, if the first value in a run of three identical values isequal to the third (i.e., if ( A[i] == A[i+2] )), then the second member ofthe run must be identical to the first and the third. To prove this

Page 33: Standish Sol Manual Ece223

Table of Contents — continued

mathematically, consider three consecutive array items inincreasing sorted order, A[i] ≤ A[i+1] ≤ A[i+2]. Now if A[i] == A[i+2], then it isimpossible for either of the strict inequalities A[i] < A[i+1] or A[i+1] <

A[i+2] to hold also. Otherwise, by the transitivity of inequality, wewould have A[i] < A[i+2], which contradicts the known truth of A[i] ==

A[i+2].

Answers to Review Questions 5.3

For all the answers, let us assume that we are trying to prove programP correct.

1. Assertions are logical statements that specify: (a) the conditionsthat hold before a program is executed, (b) the net effectachieved after executing a program, or (c) the state of affairsachieved inside a program at various intermediate points duringits execution.

2. The precondition describes the conditions that hold true before Pis executed.

3. The postcondition describes the conditions that hold true after Pis executed.

4. A proof of correctness of program P is a sequence of logicaldeductions that establishes the truth of the postcondition,assuming the truth of the precondition initially, and usingassertions internal to the program which capture effects ofperforming individual actions inside the program during itsexecution. Logically rigorous rules of inference must be usedproperly to achieve a valid correctness proof.

5. A loop-invariant is an assertion inside a loop that it is always true,no matter how many times the loop has been executed.

6. Loop-invariants capture the essence of what a loop is supposed todo and what it maintains always to be true throughout itsexecution. Also, loop-invariants along with preconditions andpostconditions may aid us in proving loop termination after afinite number of steps.

Solutions to Selected Exercises in Exercises 5.31. There exists K and there exists n0 such that if K is greater than 0

and n0 is greater than 0 then it is true that for all n if n is greaterthan n 0 then|g (n)| < K |f(n )|. [Note: This condition is used in thedefinition of O-notation. cf p. 221.]

Page 34: Standish Sol Manual Ece223

Table of Contents — continued

2. There exists k such that 1 ≤ k ≤ n and for all i, if 1 ≤ i ≤ n, then A[k] ≥A [ i]. [Note: this describes A[k ] as the maximum element in thearray A[1:n]].

3. ∀ ε [(ε > 0) ⊃ ∃ δ [(δ > 0) ⊃ [ (|x – c|< δ) ⊃ (|f(x) – f(c)| < ε )] ] ].

4. ∃ k [((1 ≤ k )∧ (k ≤ n )) ⊃ ∀ i [((1 ≤ i )∧ (i ≤ n )) ⊃ (A[k] ≤ A[i] ) ] ]

5. ~∃ K [∃ n0 [((K > 0 )∧ (n0 > 0 )) ⊃ ∀ n[(n > n0 ) ⊃ (|g(n)| < K|f(n)|)] ] ] ⇒∀ K [∀ n0 ~ [~((K > 0 )∧ (n0 > 0 )) ∨ ∀ n[(n > n0 ) ⊃ (|g(n)| < K|f(n)|)] ] ]⇒∀ K [∀ n0 [~~((K > 0 )∧ (n0 > 0 )) ∧ ∼∀ n[~(n > n0 ) ∨ (|g(n)| < K|f(n)|)] ] ]⇒∀ K [∀ n0 [((K > 0 ) ∧ (n0 > 0 )) ∧ ∃ n[~~(n > n0 ) ∧ ~(|g(n)| < K|f(n)|)] ] ]⇒∀ K [∀ n0 [ (K > 0 ) ∧ (n0 > 0 ) ∧ ∃ n[(n > n0 ) ∧ (|g(n)| ≥ K|f(n)|)] ] ].

Translating the later into English yields, “For any K >0 and any n0 >0there exists an integer n greater than n0 such that |g(n)| ≥ K|f(n)|. Inessence, this says that if f(n) and g(n) are positive functions, theng(n) grows so fast as n increases that it is never bounded above bysome constant multiple of f(n) for sufficiently large n. This wouldbe a true statement, for instance, if g (n ) = n2 and f(n ) = n . Forexample, for any K > 0 and any n0 > 0, choose n > max(K, n0). Then n >n0 and n >K, and multiplying both sides of n > K by n, we get n2 > K nwhich is the same as |g(n)| > K|f(n)|, from which it follows that |g(n)|≥ K|f(n)|.

6. Loop invariant : [((0 ≤ i )∧ (i < n )) ∧ ( S == ∑(0 ≤ j ≤ i) A [j]) ]

Precondition : i == 0, S == 0.

Postcondition : the value returned by the function Sum == ∑(0 ≤ i ≤ n

– 1) A[i]

The following version of the original program is annotated withassertions:

| int Sum(InputArray A, int n) /* assume n > 0 */| {| int i = 0, S = 0;|

5 | {♦ ( S == 0 ) ∧ ( i == 0 ) ∧ ( n > 0 )♦ } /* precondition */| while (i < n) {| {♦ ( 0 ≤ i < n ) ∧ ( S == ∑ (0 ≤ j < i) A[j] ) ♦ }| S += A[i];| {♦ ( 0 ≤ i < n ) ∧ ( S == ∑ (0 ≤ j ≤ i) A[j] ) ♦ } /* loop invariant */

10 | i ++;| }| {♦ ( i == n ) ∧ ( S == ∑ (0≤ j ≤ n–1) A[j] ) ♦ }| return S| {♦ ( Sum == ∑ (0≤ j ≤ n–1) A[j] ) ♦ } /* postcondition */| }

Page 35: Standish Sol Manual Ece223

Table of Contents — continued

Without loss of generality, we can assume (n > 0) as part of theprecondition. Otherwise, if n ≤ 0, the while-loop on lines 6:11 neverexecutes, and the sum becomes zero, which is the correct value ofthe sum of the integers in the array A[0:n–1] when the range 0:n– 1is empty. (The range 0:n–1 is empty whenever n ≤ 0, but if n > 0,the range 0:n–1 is non-empty and contains at least the item A[0].)

We proceed to prove correctness by the method of induction:

Base Case : Before entering the while-loop for the first time, S== 0 and i == 0. In particular, S == ∑(0 ≤ j < i) A [j] == ∑(0 ≤ j < 0) A [j]= 0, because there are no integers j in the range 0 ≤ j < 0. Hence,the assertion on line 7 is true on the first trip through the while-loop. Then on line 8, the statement S += A[i] is executed, with theprevious values S == 0 and i == 0. So A[0] is added to S and S ==A [0 ] == ∑(0 ≤ j ≤ i) A [j]. From this, the truth of the loop invariant online 9 follows on the first trip through the loop.

Induction Step: Assume that the invariant holds when we are inthe ith iteration of the loop. Then the invariant on line 9 holds,namely:

{♦ ( 0 ≤ i < n ) ∧ ( S == ∑ (0≤ j ≤ i) A[j] ) ♦ } /* the loop invariant */

When the statement i++ on line 10 is executed, the value of i isincreased by 1. At this moment, the assertion ( S == ∑ (1 ≤ j ≤ i) A[j] ) isnot true for the newly increased value of i, but rather it is trueonly for the previous value of i which was (i – 1). So to maintain thetruth of this assertion after incrementing i by 1, we need a newversion in which we substitute (i – 1) for i, giving:

{♦ ( S == ∑ (0 ≤ j ≤ i-1) A[j] ) ♦ }

Because i and j are integers, this simplifies immediately to:

{♦ ( S == ∑ (0 ≤ j < i) A[j] ) ♦ }

We now return to the beginning of the while-loop on line 6 totest the while-condition. If the newly increased value of i is stillless than or equal to n, i.e., provided (i < n), we enter the while-loop for the (i + 1)st iteration, knowing that the assertion (i < n) istrue (because the while-condition was true in order to enter thewhile-loop) and that ( S == ∑ (0 ≤ j < i) A[j] ) which held true for theincremented value of i at the end of the last trip through thewhile-loop. These two facts, together with the fact that (0 ≤ i) ,since i started at 1 and was possibly incremented on previous

Page 36: Standish Sol Manual Ece223

Table of Contents — continued

iterations of the loop, establish the truth of the assertion on line7. Executing the statement S += A[i] on line 8 now adds a new lastterm A[i] to the value (S == ∑ (0 ≤ j < i) A[j] ), yielding the value ( S == ∑ (0 ≤ j ≤

i) A[j] ), which establishes the truth of the loop invariant on line 9.

When we fall out of the while-loop on the last trip, the value of ihas been incremented to be n, and the value of S on the previoustrip through the loop will have been ( S == ∑ (0 ≤ j < n) A[j] == ∑ (0 ≤ j ≤ n–1) A[j]

), which establishes the truth of the assertion on line 12. Finally,the value of S is returned as the value of the function Sum on line13, establishing the validity of Sum == ∑ (0≤ j ≤ n–1) A[j] on line 14. Thelatter can be converted to the exact original postcondition by achange of dummy variables inside the summation from j to i.

This proves that the program correctly computes the sum of allelements in the array A[0:n–1].

Finally, we need to establish is that the loop terminates. This iseasy to prove because the loop variable i starts at 0 andthereafter is always incremented. Since n is finite and positive,the last in a continually increasing sequence of values of i willeventually reach it, causing termination of the loop in a finitenumber of iterations.

7. Let us prove this by induction as well. Here is the outline.

Base case: Fact(0) == 1, which the program computes correctly,since the input n == 0 causes the if-condition (n > 0) on line 7 toevaluate to false, after which the statement in the else-part online 13 is executed, returning the result 1.

Induction step: Let us assume that Fact computes the factorialfor all integers which are less than n.

∀ i [(0 ≤ i < n) ⊃ (Fact(i) == 1*2*3*...*i ) ]

We now attempt to prove that factorial of n is correctlycomputed by Fact.

Taking i == (n – 1) in the inductive assumption, we get

Fact(n – 1) == 1*2*3*...*(n – 1)

Substituting this value for Fact(n – 1) on the right side of theassignment statement Fact == n * Fact(n – 1) on line 9 of the program,and executing this assignment statement yields the valuen*(1*2*3*...*(n–1))== 1*2*3*...*(n – 1)*n for Fact(n).

Finally, we know that since n is finite and non-negative, Fact willterminate after a finite number of recursions, because eachsuccessive recursion diminishes the value of n by 1 until n reaches

Page 37: Standish Sol Manual Ece223

Table of Contents — continued

the value 0 causing the test (n > 0) to fail and causing the functionto terminate execution.

Answers to Review Questions 5.41. Program transformations can be thought of as exchange laws that

permit replacement of one form of a program with an equivalentone.

2. Yes.

3. One transformation is tail recursion elimination. Another isreplacing a function call by an appropriately substituted functionbody.

Solutions to Selected Exercises in Exercise 5.41.

| void ReverseString(char *S, int m, int n) /* to reverse the characters */| { /* S[m:n] in string S */| char c;|

5 | while (m < n) { /* while the center has characters to reverse */| c = S[m]; S[m] = S[n]; S[n] = c; /* swap the edges, then move */| m++; n– –; /* the edge indices toward the center */| }| }

In one measurement experiment the iterative string reversalprocedure shown in the answer above was compared with therecursive string reversal procedure given in the problemstatement in Exercise 5.4.1 on page 172. Both procedures weretimed when performing 10,000 reversals of a string S containingthe 26 letters of the alphabet. The iterative reversal proceduretook an average of 0.0102 ticks per reversal, and the recursivereversal procedure took an average of 0.0137 ticks per reversal.Consequently, the recursive version took 34.3% more time perreversal than the iterative version, on the average. [Note: a tick isa 60th of a second, so 0.0102 ticks = 0.17 milliseconds and 0.0137ticks = 0.228 milliseconds.]

Answers to Review Questions 5.5

1. Unit testing consists of testing individual functions in isolation tosee if they work on all expected classes of inputs. In tegrat iontesting tests the coordinated execution of functions, actingtogether in the context of the conditions expected for actualsystem operations.

Page 38: Standish Sol Manual Ece223

Table of Contents — continued

2. Acceptance testing is a practice, sometimes specified in softwareacquisition contracts, in which a completed software system mustpass a specified test in order to be accepted for delivery by theclient. Regression test ing is a practice, often used in themaintenance phase of the software lifecycle, in which, afterchanging a system, you retest it thoroughly to make sure thateverything that used to work still works in the new version.

3. In top-down testing, the top-levels of a system are tested beforetesting the components that they use. In bottom-up testing, thecomponents of the system are tested before testing other partsof the system that use them.

4. A stub is a subroutine that stands in place of another subroutine(that will be written later), and fakes its output on certain testcases. A test driver for a subroutine S, is a function thatenumerates test cases for S, calls S for each test case, checks theoutputs afterward to see that they are correct, and notes anydiscrepancies in a printed test result report.

5. A formatted debugging aid is a routine that prints a datastructure. Early in the history of software engineering, it wasdiscovered that debugging times for a software project couldoften be cut in half if formatted debugging aids were implementedfirst and then were used later when implementing and testing therest of the system.

Page 39: Standish Sol Manual Ece223

Table of Contents — continued

Solutions to Selected Exercises in Exercise 5.5

1.

InitializeAndDisplayCalculator

UserSubmittedAnExpression

UserWantsToQuit

GetAndProcessOneEvent

Display

Shutdown S1

CalculatorModule:

Evaluate

+ functions of YourCalculatorUnitS1

YourCalculationModule:

CalculatorProgramShell S2

CalculatorProgramShell:

2.

InitializeEmpty

Insert Remove

Full

S1

PQImplementation.c:

PQItemS1

PQTypes.h:

PriorityQueueSort S2

PriorityQueueSorting program:

PQListNode

PriorityQueue

Answers to Review Questions 5.6

Page 40: Standish Sol Manual Ece223

Table of Contents — continued

1. It is frequently hard to anticipate where the inefficiencies in aprogram lie. Consequently, it is important first to measure wherea system is spending its time, and then to concentrate on tuning(i.e., changing so as to improve) the parts that account for most ofthe execution time.

2. An execution profile tool (or profiler) measures where a programis spending its time during execution. A profiler develops datathat tells you what percentage of the sample execution time isspent in various parts of the code. Its results are sometimespresented graphically in the form of a histogram.

3. Experience shows that most programs consume running time in afashion that is unevenly distributed throughout the code.

4. Tuning is a cyclical process of rewriting portions of a program toattempt improvement and then measuring the results to see if theexpected improvements were achieved.

5. Small differences, such as declaring frequently used variables ofloops as double precision integers or using floating point divisionof two integers instead of integer division (on a machine withsimulated floating point instructions) can increase the timeconsumed by a program.

Solutions to Selected Exercises in Exercise 5.6

1. On line 8 of the program the floor call is used after the divisionoperator “/”. The division operator “/” operator with floatingpoint denominator 2.0 uses floating point division and returns afloating point result after first converting its integer- valuednumerator (m + n) to a floating point number. The result is thentruncated using the floor function to eliminate the fractional part ofthe positive floating point result, and is then converted back tointeger form using the type cast with (int). Machines which simulatefloating point operations use on the order of 30 to 50 non-floatingpoint instructions to simulate each floating point instruction.Hence, on a machine with simulated floating point, it would bebetter to use integer division (by using an integer denominator 2

in Middle = (m + n) / 2. This would eliminate the costly simulatedfloating point operations.

2. [Note: This exercise can be used as the basis for an informativelaboratory experiment in a CS2 Lab section.]

Answers to Review Questions 5.7

Page 41: Standish Sol Manual Ece223

Table of Contents — continued

1. The case for advocating the building of software from reusablesoftware components is based on its economic advantage. Thecost of building software is an exponential function of its size.Since software size is the biggest cost driver in a softwareproject, if you can reduce the size of what you have to build youcan best reduce the cost. Hence, if you can build your softwarefrom reusable parts, you can often reduce the overall cost ofsystem construction substantially.

2. An equation estimating the effort required to build a softwaresystem in the most familiar class of software systems in Boehm’sCOCOMO model gives the number of person months (PM) required tobuild a system in terms of the size of the system, as follows:

PM = 2.4*(KDSI)1.05

where the quantity KDSI refers to the Kilo-Delivered SourceInstructions in the system.

3. In bottom-up programming the strategy is to implement general-purpose software components first, and then to assemble themtogether into higher-level systems. Under some economicconditions — associated with developing proprietary reusablesoftware component libraries — bottom-up programming may beuseful because it yields proprietary advantage useful for buildingother systems and cultivates in-house expertise.

Solutions to Selected Exercises in Exercise 5.71. It is cheaper to implement VisiPhysics by buying and using the two

libraries. The economics can be worked out using Boehm’sequation:

• Cost of creating VisiPhysics from scratch =

((2.4*(56)1.05/12)*$100,000) = $1,369,706

• Cost of creating VisiPhysics using libraries =

((2.4*(32)1.05/12)*$100,000)+$300,000 = $1,061,093.

Thus, the savings realized through software reuse are $308,613 inthis instance.

Answers to Review Questions 5.81. It is better to have few global variables and to have good

communication between subroutines to minimize the chances thatseparate routines will accidentally clobber global variables usedas communication devices between other subroutines. Withoutglobal variables, data flows cleanly between subroutines and theircallers. This helps make subroutines fit together nicely in the

Page 42: Standish Sol Manual Ece223

Table of Contents — continued

system design and reduces the risk that use of global variables ascommunication devices poses with respect to accidentalclobbering of communicated values through unintendedinterference.

2. Use of named constants is sensible because it enables easy changeof constant values in just one location. Moreover, all uses of theconstant value are changed accordingly. This minimizes the effortneeded to locate and change each of a number of separateconstant values spread throughout the program text and itminimizes the risk that some of the separately expressedconstant values spread throughout the program text would begiven incorrectly.

3. Two reasons are:

(a) If everyone programs in the same style, each programmerwill be able more easily to read codewritten by the others.

(b) Error detection during code reviews will be easier, sincemistakes will be easier to spot in asingle uniform style than in many separate styles.

4. There are a lot of people who will use, read, or modify a bigsoftware system with a long service lifetime. Some may use itextensively, exploring every bit of power that the software cangive them in their work. These people will require detaileddocumentation of the software’s user features. Others may useonly minimal simple features of the software, i.e., only the partsneeded in their work. These people will require an easy-to-readtutorial or a simple, easy-to-comprehend version of the user’smanual. Others will be maintenance programmers who needdetailed explanations of how the code works. Yet others will bemanagers who need good overviews of how the system’s parts fittogether, but who would get lost in the details if details were allthat were available. A single form of documentation cannotsimultaneously satisfy all of these needs.

Solutions to Selected Exercises in 5.81. This program computes the greatest common divisor (gcd) of m

and n. Hence, we can redefine the function and write it as follows:

| int gcd(int m, int m) /* where “gcd” stands for the */| { /* greatest common divisor */| if (n == 0) {| return m; /* the gcd is m, when n == 0 */

5 | } else { /* otherwise, compute the gcd of n and the */

Page 43: Standish Sol Manual Ece223

Table of Contents — continued

| return gcd (n, m % n) /* remainder of m after division by n */| }| }

2. This program writes out the successive digits of its non-negativeinteger input. Hence, we can call it PrintDigits:

| void PrintADigit(int digit) /*prints the character corresponding */| { /* to an integer value == digit */| printf("%c", (char)((int)('0') + digit)); /*not followed by a newline */| }

5 || void PrintDigits(int n) /*let n be a non-negative integer */| {| if (n < 10) { /*if there is just one digit in n, then */| PrintADigit(n); /*print it */

10 | } else { /*otherwise, first print the digits other */| PrintDigits(n / 10); /*than the last, and then */| PrintADigit(n % 10); /*print the last digit */| }| }

3. Use the input sequence: 0.3, 0.4, 1.1, –2.3, 99999, 99999. This sequenceforces the program to add 99999 to the sum giving a wrong answer,25000.20.

4. Use a transformation that eliminates the duplicated assignmentstatement,

| PlayersMove = GetMoveFromUser( );

and places it first inside a while-loop that uses a break statementto exit. Here is the transformed program:

| while (1) {| PlayersMove = GetMoveFromUser( );| if (PlayerMove == QuitSignal) {| break;

5 | }| DisplayPlayersMoveOnTheBoard( );| MakeMachineMove( );| }

Page 44: Standish Sol Manual Ece223

Table of Contents — continued

Answers to Review Questions 6.21. When we pose the question, “What do we use for a yardstick?” we

mean how can we measure and compare algorithms meaningfullywhen the same algorithm runs at different speeds and will requiredifferent amounts of space when run on different computers orwhen implemented in different programming languages?

2. When an algorithm is written in different programming languagesand implemented on different machines, both the execution timeand the space consumed may differ. However, when implementingan algorithm on different machines using different software, whatdoes not change is the family of growth curves which plot resourceconsumption as a function of the size of the algorithm’s inputs. Thebasic shape of these curves will remain the same within the familyof resource consumption curves.

3. The part of the curve’s equation that dominates the value of thefunction (i.e., accounts for most of the function’s value) when theinput size is large is called the dominant term of the function. Forexample, suppose an2+bn+c is the equation of a curve that fitssome data. In this equation an2 is the dominant term. Supposing fora moment that a = b  = c  = 1, the term a n 2 accounts for morethan 98% of the value of an 2+ bn+ c whenever n ≥ 50, and thedecision to ignore the contribution of the lesser terms, b n + c ,would introduce an error of less than 2%. For n ≥ 100, the error isless than 1%.

4. This approach is perhaps justified because, for large input values,the dominant term determines almost all of the value of thefunction, and because the lesser terms contribute negligibleamounts that do not influence the function’s value appreciably —i.e., if the contributions of the lesser terms were ignored for largeinput sizes, only minor errors would result. Thus the approachcould be viewed as purchasing simplicity and ease ofcomprehension in exchange for the admission of negligible errorsof net effect.

Answer to Exercise 6.2

1. The data in Table 5.36 for the recursive SelectionSort Program 5.29are fit by the equation:

f(n) = 0.00024148*n2 + 0.00136*n + 0.001

Page 45: Standish Sol Manual Ece223

Table of Contents — continued

The data in Table 5.36 for the iterative SelectionSort Program 5.35 arefit by the equation:

f(n) = 0.000221*n2 + 0.0002*n + 0.015

Thus, the data are closely matched by curves that are instances ofthe basic running-time equation, f(n) = an2 + bn + c, given in Table6.5, for various values of the coefficients a, b, and c. Hence, we canconclude that the basic resource consumption curves for variousversions of SelectionSort are the same. The curves fitting the data inTables 5.36, 6.3, and 6.5 are independent of S e l e c t i o n S o r t ’ simplementation features — i.e., whether or not recursion oriteration was used, or whether while-statements, for-statements,or if-statements were used in C. They are also independent of theprogramming language used and the particular brand of computeron which SelectionSort was run.

Answers to Review Questions 6.3

1. Constant — O(1)

Logarithmic — O(log n)

Linear — O(n)

n log n — O(n log n)

Quadratic — O(n2)

Cubic — O(n3)

Exponential — O(cn) where c > 0 is a finite constant.

2. No, the complexity class tends to give useful comparativeinformation only when the input size of the algorithm iscomparatively large.

3. If we are using small data sets (in which the input size is small)then the complexity class of the algorithm does not matter verymuch. For small n, an O(n2) and an O(n log n) algorithm have almostthe same running time. On the other hand, for large sortingproblems, an O(n log n ) sorting algorithm may have dramaticadvantages in running time over an O(n2) sorting algorithm (asillustrated by the answer to Ex. 6.3.2 below). HeapSort, MergeSort, andQuickSort are O(n log n) algorithms. SelectionSort and InsertionSort a reO(n2) sorting algorithms.

4. Using algorithms that take exponential space or exponentialrunning time is usually impractical for all but small input values.However, for small inputs occasionally it makes sense to use anexponential algorithm. For example, the input sizes for which an

Page 46: Standish Sol Manual Ece223

Table of Contents — continued

exponential algorithm running in time 2n will perform better than acubic algorithm running in time n3, can be computed by solving theinequality: 2n < n3 for n. (This inequality holds for 2 ≤ n ≤ 9 but notfor n ≥ 10).

Solutions to Selected Exercises in Exercises 6.3

1. If the computation requires 2n steps at one microsecond per step,the largest problem that can be solved before the sun burns out isof size 77. Since there are 3.1536*107 seconds per year, 106

microseconds per second, and 5*109 years before the sun burnsout, there are (3.1536*107 ) * ( 1 0 6 ) * ( 5 * 1 0 9 ) = 1.5768 * 102 3

microseconds left before the sun burns out. Thus, we need to findthe largest value of n such that 2n is less than 1.5768 * 1023. Thisis gotten by finding the largest n for which the following inequalityholds true:

2n < 1.5768 * 1023, which holds if and only if (iff)

ln (2n) < ln(1.5768 * 1023), which, in turn, holds iff

n * ln 2 < ln(1.5768 * 1023), which, in turn, holds iff

n < ln(1.5768 * 1023)/(ln 2), which, in turn, holds iff

n < 77.0613459

2. Selec t ionSor t will take (262144*262144)/(106*60) = 1145.3246minutes (or a little over 19 hours) and Q u i c k S o r t will take262144*log2 (262244)/(106*60) = 0.0786432 minutes. Applying acost of $100 per minute, SelectionSort costs $114,532.46 and QuickSort

costs $7.86. QuickSort seems a tad cheaper under these particulareconomic circumstances.

3. Many practical parsing algorithms used in contemporary compilersrun in time O(n), even though theoretically it is not known how toperform completely general parsing in time less than O(n2.8).

4. There are substring searching algorithms that run O(n) time. Thereare even some that run in sublinear time, meaning that they run infewer than n steps for a text being searched that is n characterslong. There are also obvious, but inefficient, text searchingalgorithms that run in time proportional to m *n , where m is thelength of the search pattern and n is the length of the text beingsearched.

Answers to Review Questions 6.4

Page 47: Standish Sol Manual Ece223

Table of Contents — continued

5. As can be seen from Equation A.22 on page 708 of the M a t hReference appendix, changing the base of a logarithm from logb xto logc x involves multiplying logb x by a constant whose value islogc b . Hence, the logarithms of x using various different basesdiffer from one another only by constant multiples. The O-notationfor O(f(x))is insensitive to constant multiples. Therefore, the O-notation for O(log n) is conventionally given without a base.

6. Once the constant of proportionality is removed from a constantfunction, all that is left is 1. Hence, we get O(1).

Solutions to Selected Exercises in Exercises 6.4

1. f(n) is O(n log n).

2. f(n) is O(n3).

3. From the definition of the O-notation, we need to find K and n0 suchthat:

5n3+3n2+4n+8 ≤ K n3, ∀ n ≥ n0.

Letting n0 = 1, n ≥ n0 is the same as n ≥ 1, which is the same as 1 ≤n . Now, multiplying both sides of 1 ≤ n by n gives n ≤ n 2 andmultiplying both sides by n 2 gives n 2 ≤ n 3 . And putting 1 ≤ ntogether with n ≤ n2 gives 1 ≤ n2, and putting 1 ≤ n2 together with n2

≤ n3 gives 1 ≤ n3. Similarly, multiplying both sides of 1 ≤ n2 by ngives n ≤ n3. So if n ≥ 1, we have four consequences: (a) n2 ≤ n3, (b) n≤ n3, (c) 1 ≤ n3, and (d) n3 ≤ n3. Now,

multiplying inequality (a) by 3 gives 3n2 ≤ 3n3,

multiplying inequality (b) by 4 gives 4n ≤ 4n3,

multiplying inequality (c) by 8 gives 8 ≤ 8n3, and

multiplying inequality (d) by 5 gives 5n3 ≤ 5n3 .

Then adding up these inequalities gives: 5n3+3n2+4n+8 ≤ 20 n3.

Hence, if we choose K = 20, then for all n ≥ 1 (i.e., ∀ n ≥ n0) we haveproven that 5n3+3n2+4n+8 ≤ K n3, which formally establishes that5n3+3n2+4n+8 is O(n3).

4. From the definition of the O-notation, we need to find K and n0 suchtha t

7*2n+9*n3 ≤ K 2n, ∀ n≥ n0.

This is the same as asserting that 7+9*n3/2n ≤ K, ∀ n≥ n0, which,in turn, is the same as asserting that n3/2n ≤ (K – 7)/9, ∀ n≥ n0.

Page 48: Standish Sol Manual Ece223

Table of Contents — continued

If we choose K = 16, then (K – 7)/9 = (16 – 7)/9 = 9/9 = 1. So,wecan then ask if we can find some n0 such that ∀ n≥ n0, n3/2n ≤ 1.Choosing n 0 = 10 is sufficient because 103/210 = 1000/1024 =0.9765625 ≤ 1. Now we can work backwards to put the final resulttogether, as follows.

We know that for n0 = 10, any n≥ n0, implies n3 ≤ 2n, which is thesame as n3 ≤ 1*2n, which is the same as, n3 ≤ (16 – 7)/9*2n, which,in turn, is the same as: 9*n3 ≤ 16*2n – 7*2n, which is the same as,7*2n + 9*n3 ≤ 16*2n. This last inequality is equivalent to 7*2n +9*n3 ≤ K*2n, which holds ∀ n≥ n0. Thus, we have proven formally that7*2n+9*n3 is O(2n).

5. From the definition of the O-notation, we need to find K and n0 suchtha t

log(n+1) ≤ K*log n, ∀ n > n0.

First, choose n0 = 2 and then choose K = 2. Thus, for any n ≥ n0, wehave 2 ≤ n ⇒ 2n ≤ n2 ⇒ (n + n) ≤ n2 ⇒ (n + 1) ≤ n2. Now taking base blogarithms of both sides of the latter inequality (for any arbitrarybase b > 1) gives, logb(n+1) ≤ logb(n2). But since logb(n2) = 2*logbn, the latter inequality becomes logb(n+1) ≤ 2*logb(n), for all n ≥ n0.Hence log(n+1) is O(log n).

Answers to Review Questions 6.5

1. O(log n) — Binary Search

O(n) — Sequential Searching

O(n log n) — MergeSort

O(n2) — SelectionSort

O(2n) — Solution to the Towers of Hanoi problem

2. One way of solving the recurrence is to use unrolling and summing.First, because the general recurrence exchanges T(n ) for anexpression involving T(n /2), it is useful to assume n is of thegeneral form n = 2k, for k ≥ 0, because this is the only form n cantake which allows it to be evenly divided by 2 repeatedly. Then wecan rewrite T(n) = 3n + 2*T(n/2) in the form, T(2k) = 3*2k + 2*T(2k–1). If we unroll T(2k) a number of times, say i times, and thensimplify, we notice that it takes the form, T(2k ) = i* ( 3 *2 k ) +2i*T(2k– i). Then, when i = k, we get T(2k) = 3*k*2k + 2k*T(2k–k) .But since, T(2k–k) = T(20) = T(1) = 2, the latter can be rewritten asT(2k) = 3*k*2k + 2*2k. Now solving n = 2k for k (which yields k = lg

Page 49: Standish Sol Manual Ece223

Table of Contents — continued

n) and then substituting lg n for k and n for 2k in the last form ofthe equation for T(2k) yields the final solution, T(n ) = 3 n  lg n+2 n.

Another way to solve the recurrence is to use the generalsolution table (given as Table 6.21 on page 241 and also on thebottom of the last panel of the Math Survival Guide). Aftermatching the equation for T(n) to the general form given in thesolution table, we find that a = 2, b = 3, c = 0, d = 2, and p = 2.Then, since, d  = p , we select the general solution form thatapplies under the condition d = p, substitute the values of a, b, c,d, and p, and simplify to obtain, T(n) = 3 n lg n+ 2 n.

Yet a third way of solving the recurrence relations is to use themethod of summing factors explained on pages 725 to 726 of theMath Reference appendix.

3. Recurrence relations give a relationship between the value of ageneral term for n , T(n ), and the values of T(k ) for variousarguments k less than n. In the method of unrolling and summing,we expand the general term for T(n ) repeatedly, substitutingexpressions for T(n ) given by the right side of the generalrecurrence relation for T(n), until the expansion stops by reachingbase cases. This process of expansion is called unrolling.Then wesubstitute the values for the base cases and sum up the unrolledexpression into a closed form, which is then simplified to get thegeneral solution.

Solutions to Selected Exercises in Exercises 6.5

2. Starting with Recurrence Relations 6.6 (on p. 236) which are,

T(1) = a

T(n) = b n + c + d T(n – 1)

and unrolling, we get:

T(n) = b n + c + d T(n – 1)

= b n + c + d [b(n–1) + c + d T(n – 1)]

= b n + c + d b(n–1) + d c + d2T(n – 2)] .

Page 50: Standish Sol Manual Ece223

Table of Contents — continued

Reexpressing the last line with powers of d i used asmultipliers, gives:

= d0b(n) + d0c +

d1b(n– 1) + d1c + d2T(n – 2) .

Then, unrolling some more, we get:

= d0b(n) + d0c +

d1b(n– 1) + d1c +

d2b(n– 2) + d2c +

: :

di–1b(n– (i – 1)) + di–1c + di T(n – i)

: :

So letting i = n – 1, the unrolling stops on the line reachingthe base case:

dn–2b(n – (n – 2)) + dn–2c + dn–1 T(n – (n – 1)) .

But, T(n – (n – 1)) = T(1) = a. So the last line can be rewrittenas:

dn–2b(n – (n – 2)) + dn–2c + adn–1 .

Then, summing all the columns in the unrolled equation gives:

T(n) = b∑i=0

n–2

di(n–i) + c∑i=0

n–2

di + adn–1 .

And this can be slightly rearranged to yield Equation A, asfollows:

T(n) = adn–1 + (bn + c)∑i=0

n–2

di – b∑i=0

n–2

    i   d i .

(A)

[Note: We can also solve recurrence relations 6.6 using the methodof summing factors.] Each of the following lines is multiplied byprogressively higher powers of d . That is, when we see thenotation: d i *{T(n – i) – d T(n – i – 1) = b (n – i) + c }, it signifiesmultiplying all terms on both sides of the equation by di:

T(n) – dT(n–1) = b n +c

d*{T(n–1) – dT(n–2) = b(n–1)+c}

d2*{T(n–2) – dT(n–3) = b(n–2)+c}

: : :

: : :

dn–2*{T(2) – dT(1) = b(2) +c}

Page 51: Standish Sol Manual Ece223

Table of Contents — continued

When we sum the above equations, we get a telescoping sum onthe left in which all but the first and last terms cancel each other:

T(n) – dn–1T(1) = b∑i=0

n–2

di(n–i) + c∑i=0

n–2

di .

The latter can again be rearranged to give Equation A, again:

T(n) = adn–1 + (bn + c)∑i=0

n–2

di – b∑i=0

n–2

    i   d i .

(A)

Now that we have gotten Equation A by two separate methods,let’s derive the two special cases given in Table 6.18 (on page236). Namely, the cases for d = 1 and d ≠ 1.

First, let’s solve the last Equation A for the case that d = 1.Noting that, if d = 1 then di = 1 for any i, we can rewrite EquationA as:

T (n ) = a + (b n + c ) ∑i=0

n–2  1 – b ∑

i=0

n–2    i   , which

simplifies first to,

T(n) = a + (bn + c)(n – 1) – b  (n – 2)(n – 1)

2   .

A fter more simplification, the last line yields the final form givenin Table 6.18:

T(n) = (b/2)n2 + (b/2 + c)n + (a – b – c), provided d = 1.

Now we consider the case that d ≠ 1. Returning to Equation Aabove, we see that some terms are geometric progressions. Forexample,

∑i=0

n–2di is a geometric progression with a sum equal to

dn–1 – 1d  –   1

.

Also, one of the terms in Equation A is of the form ∑(1≤ i≤n) i*d i.Using the hint that ∑(1≤ i≤n) i*d i = [d/(d–1)2]*[(nd–n–1)dn+1 ], andtaking this sum with an upper limit of (n–2) instead of n, yields:

b∑i=0

n–2

idi = b*[d/(d–1)2]*[((n–2)d–(n–2)–1)dn–2+1].

Now, substituting these results for ∑d i and ∑id i in Equation Agives:

Page 52: Standish Sol Manual Ece223

Table of Contents — continued

T(n) = adn–1 + (bn+c) dn–1 –  1d  –   1 – b*[d/(d–1)2]*[((n–2)d–(n–2)–

1)dn–2+1] .

The last equation is in closed form. After (lots of) simplification,this equation can be rearranged to take the final form in Table6.18:

T(n) = \B (\F(a – b – c,d) + \F(b + c,d – 1) + \F(b,(d – 1)2)) dn –

b

d   –   1 n  +

c   –   (b  +   c )d

(d  –  1) 2 , for d ≠ 1.

3. We proceed to set up and solve some recurrence relations for therunning time. Let T(n) be the time required to sort an array A[0:n–1]

of n items. In the abstract strategy for recursive SelectionSort,given in Exercise 6.5.3, if n == 1 and we call SelectionSort(A,1) to sortthe array with one item, A[0:0], the function returns without doingany sorting, after performing the test on line 3. It takes a constantamount of time, say a, to call and return from the function and toperform the test on line 3. Hence, we can write a base case for ourrecurrence relations, T(1) = a. Proceeding now to characterize thegeneral recurrence T(n), for selection sorting an array A[0:n–1] o fsize n, we annotate the abstract strategy with costs, as follows:

| void SelectionSort(SortingArray A, int n) /* To sort A[0:n–1] */| {| if ( n > 1) { /* if the array A[0:n–1] has more than one item */| Cost = bn + c1; /* Find position, p, of smallest item in A[0:n–1] */

5 | Cost = c2; /* Exchange A[p] and A[n–1] */| Cost = T(n – 1); /* Sort the rest of the array A[0:n–2] recursively */| }| }

We comment on these annotations as follows. First, on line 4, thecost of scanning all n items in the array A[0 :n–1 ] to locate theposition p of the smallest item takes linear time of the form bn +c1, (where b and c1 are constants). It then takes a constant amountof time, c2, on line 5 to exchange the items in A[p] and A[n–1]. Finally,on line 6, the SelectionSort procedure is called recursively usingthe procedure call SelectionSort(A, n – 1). This takes time T(n – 1).

Letting c = c1 + c2, and summing the costs on lines 4:6 yields:

T(n) = b n + c + T(n – 1) .

Putting this together with T(1) = a , we get the followingrecurrence relations:

T(1) = a

T(n) = b n + c + T(n – 1) .

Page 53: Standish Sol Manual Ece223

Table of Contents — continued

Consulting Table 6.18 on page 236 (or the solution on the top ofthe last panel of the Math Survival Guide) and noting that d = 1, wecan read the solution to the running time equation as follows:

T(n) =

b

2  n2 +

b

2 +  c n + (a – b – c).

Thus, the running time for recursive SelectionSort is O(n2).

4. The recurrence relations (from Eqs. (6.9) on p. 240) are:

T(1) = a

T(n) = b n + c + d T(n / p), where p > 1, d > 0

If we assume n is evenly divisible repeatedly by powers of p, thenn takes the form n  = p k , which implies k  =   logp  n . Using thisassumption, then unrolling and summing gives Equation (6.10) onpage 240, which is rewritten as Equation (B) below:

T(n) = d0 bn/p0 + d0 c  +

d1 bn/p1 + d1 c  +

d2 bn/p2 + d2 c  +

d3 bn/p3 + d3 c  +

. . .

d (k – 1) bn/p (k – 1) + d (k – 1) c + dk T(n/pk)

T(n) = bn ∑j= 0

k–1 (d j/p j) + c ∑

j= 0

k–1dj + dka (B)

We now derive the four special cases of the latter equationgiven in the table of solutions (in Table 6.21, on p. 241).

First Case:

Assume d = p. Then dk = pk = n, k = logp n, and (d j/p j)= 1. So,Equation B simplifies to:

T(n) = bnk + c 

d k  –   1

d  –   1 + adk .

And substituting n for dk and logp n, for k in the latter gives:

T(n) = bn logp n + c(n – 1)d  –   1 + an

which can be rearranged into the final form given in Table 6.21:

Page 54: Standish Sol Manual Ece223

Table of Contents — continued

T(n) = bn logp n +

a   +  c

d  –   1 n – c

d  –   1 .

Second Case: d = 1

Assume d = 1. Then dk = 1, pk = n, k = logp n, and (dj/pj)= (1/pj).So, Equation B simplifies to:

T(n) = bn 

1/p k  –   1

1 /p  –   1 + ck + a

= bn 

1/n  –   1

1 /p  –   1 + c logp n + a

= bn 

(n  – 1) /n

(p  –  1) /p + c logp n + a

= bp

(p  –   1 )(n – 1) + c logp n + a

which simplifies to the final form:

T(n) =

bp

p  –   1  n + c logp n +

a   –  bp

p  –   1 .

Third Case: d = 1, b = 0

Assume d = 1 and b = 0. Then dk = 1, pk = n, and k = logp n. So,Equation B simplifies to:

T(n) = ck + a

and substituting logp n for k yields the final form:

T(n) = c logp n + a .

Fourth Case: d ≠ 1, d ≠ p

Assume d ≠ 1 and d ≠ p. Then pk = n and k = logp n. So, Equation Bsimplifies to:

T(n) = bn 

d k /p k  –   1

d /p  –   1 + c 

d k  –   1

d  –   1 + adk

= bn 

(d k – n )/n

(d  –  p )/p + c 

d k  

d  –   1 –

c

d  –   1 + adk

=

bp

d  –  p   +  c

d   –   1  +   a  dk –

bp

d  –  p  n –

c

d  –   1

To get the final form of the solution, we need to reexpress dk inthe form nlogpd. To see why this holds, we note the following seriesof equivalences: 1 = 1 ⇔ logp  n / l og p  n = logp  d / l og p  d ⇔l og p  d * log p n = logp n * log p d ⇔ logp n log p d = logp d log p n ⇔nlogpd = dlogpn ⇔ nlogpd = dk. Hence, we can write the final form:

Page 55: Standish Sol Manual Ece223

Table of Contents — continued

T(n) = \B(a  +  \F(b p ,d – p ) + \F(c ,d – 1 )) n logp d –

bp

d  –  p  n –

c

d  –   1 .

5. (a) O(n log n)

(b) O(n)

(c) O(log n)

(d) O(nlogpd) or O(n) depending on which one is greater.

6. Equations 6.7 on page 237 match the conditions for the first line ofTable 6.21 on page 241, if we set d = p = 2. The solution resultingfrom substituting 2 for both d and p in the first line of Table 6.2.1is:

T(n) = b n log2 n + (a + c) n – c

This equation gives the running time of MergeSort and is identicalEquation 6.8 on page 239.

7. The proof that log2(n + 1) < log2(n) + 1, for all n > 1, is given onpages 709 to 710 of the Math Reference Appendix. (A briefsummary of the steps in this proof is: (n > 1) ⇔ (1 < n) ⇔ (n + 1) <2 n ⇔ lg(n + 1) < lg(2 n) ⇔ lg(n+1) < lg 2 + lg n ⇔ lg(n+1) < lg n + 1).Now let’s take the running time equation for binary search givenby Equation 6.14 on page 245:

C(n) = 2 log2(n + 1) – 3 + 2 log2(n + 1)/n

Applying the inequality log2(n+1) < log2 n + 1 to this, we get,

C(n) < 2 log2 n + 2 – 3 + 2 (log2 n)/n + 2/n .

Noting that both of the terms 2(log2 n)/n and 2/n are less than 1for n > 5, we can simplify the last line above to,

C(n) < 2 log2 n + 1 .

Because the dominant term on the right side of the latterinequality is 2 log2 n, we can conclude that the average number ofcomparisons used in binary searching is O(log n).

Answers to Review Questions 6.6

1. It is often, though not always, inappropriate to apply theconclusions normally derived from O-notation when small problemsizes are being considered.

Page 56: Standish Sol Manual Ece223

Table of Contents — continued

2. A good idea is to try measurement and tuning to optimize theperformance of the algorithm for small-sized problems.

Solutions to Selected Exercises in Exercises 6.6

1. [Note: This exercise makes a good laboratory assignment for a CS2course.]

Page 57: Standish Sol Manual Ece223

Table of Contents — continued

Page 58: Standish Sol Manual Ece223

Table of Contents — continued

Answers to Review Questions 7.21. Stacks are useful for processing nested structures because we can

use a stack to interrupt processing of a given level of a nestedstructure in order to process an immediately deeper level. Whenwe have finished processing the immediately deeper level, we canthen resume the interrupted processing of the given level. Inorder for this to work, we need to push a record on the stack whichprovides enough information to resume processing of the originalinterrupted level. If the immediately deeper level itself containsadditional nested levels of the structure, all records forprocessing nested substructures will have been popped from thestack by the time we return to the original interrupted level toresume processing. Thus, stacks can accommodate processing ofnested structures with an arbitrary number of levels of nestedsubstructures.

2. Some examples of nested structures: subroutine calls withinsubroutines, subexpressions inside algebraic expressions,outlines of numbered items containing suboutlines of numbereditems, and finally, blocks within blocks and loops inside loops in aprogram written in a programming language.

3. Push-down automata can recognize sentences in context-freelanguages according to the theory of formal languages. Stacks areused as memory-structures in push-down automata, which play akey role in formal language theory. As a consequence, syntaxanalyzers for programming languages often employ push-downstacks to accomplish recognition of the structure of computerprograms during compilation.

4. Yes, stacks can be used to process function calls. Stacks are oftenused in various algorithms to hold information about postponedobligations for further processing. A stack of activation recordscan be used to keep track of a sequence of function calls duringprogram execution. Each time a function call is made, an activationrecord for the call is pushed onto the stack. This activation recordcontains information about how to resume execution of its callerafter its own execution is finished. A stack is a good datastructure for this purpose, since function calls are dynamicallynested (in time, during the running of a program).

Solutions to Selected Exercises in Exercises 7.2

Page 59: Standish Sol Manual Ece223

Table of Contents — continued

1. Whenever a tray of weight L is added to the stack, the spring isdisplaced downward by distance d = α L, which is exactly equal tothe thickness d of the tray. Therefore, the top of the stackremains at the same height.

2. Let them grow toward each other, starting at opposite ends of thearray. E.g., let S1 grow from A[0 ] upward so that S1’s secondelement will be placed in A[1 ], and so forth. S2 should grow fromA [n – 1] downward so that S2’s second element will be placed inA [n – 2], and so forth. The positions of the tops of the two stacksshould be maintained in order to determine when space in A[0:n–1]is exhausted.

Page 60: Standish Sol Manual Ece223

Table of Contents — continued

Answers to Review Questions 7.31. Two different structuring methods used in C are: the formation of

structs and arrays. In addition, using pointers to components andembedding pointers inside other composite data structures, suchas structs or arrays, enables us to create a class of linked datastructures in C.

2. We can define a C module consisting of a separately compiledsource file in C that specifies the underlying representation of anADT and that implements the required abstract ADT operations.The functions exported by this module can be specified in a headerfile for the module giving ex te rn declarations for the functionprototypes being exported in the module interface. The headerfile can be included, using an #include directive at the beginning ofanother C source program using the ADT. The ADT module itselfmay import an ItemType specifying the type of item that can be usedinside the ADT from another header file giving the typedefs of datastructures used in the data representation of the ADT.

3. You can be sure that you have used proper modular programmingpractices when defining and using an ADT if you do not have tomake changes in the ADT’s interface design when changing theunderlying ADT representation. If the ADT’s data representationhas to be changed, and you have followed proper modularprogramming practices, then you will not have to make globalchanges external to the program text defining the ADT. Rather, thechanges will be local to the ADT’s defining program text. Modularprogramming wil l also ensure that the details of theimplementation are hidden and the ADT will be used only viafeatures defined and made externally accessible in its interface.

4. No. By looking at the interface files in Programs 7.3 and 7.4 youcannot tell whether the underlying data representations for stacksand queues are linked or sequential. In fact, Programs 7.8 and 7.9given later in Chapter 7 specify sequential and linked stackrepresentations sharing the same Stack ADT interface given inProgram 7.3, and Programs 7.19 and 7.20 specify sequential andlinked queue representations sharing the same Queue ADTinterface given in Program 7.4.

Solutions to Selected Exercises in Exercises 7.31. Here is the representation of an ordered sequence <a, b, c> as a set

<a, b, c> = { {2,b}, {1,a}, {3,c} }.

Page 61: Standish Sol Manual Ece223

Table of Contents — continued

This gives us method for representing an ordered sequence of realnumbers. The integer 2 defines the position at which the realnumber b has to be placed in the ordered sequence of realnumbers. No matter where in an unordered set {2,b} appears (suchas before {1,a} in the above example), its position in the orderedsequence is unambiguously defined.

2. The representation specified in the answer to Exercise 7.3.1 wouldnot be efficient to use in order to insert or delete a new last itembecause all unordered pairs of the form {i, x i} would have to bescanned either to determine the number n of the last item todelete or to determine the number n +1 to use in a new pair {n+1,xn+1} to be added. Likewise, it would be inefficient to use if youwanted to delete the first item {1,x1} because, after deleting thefirst item, all other items {i, xi} would have to be renumbered as {i– 1, xi}, forcing you to scan and change the entire remainder of theset in order to delete the first item. Consequently, the suggestedrepresentation would be suitable neither for stacks nor forqueues. However, if you bundled the set of unordered pairs as amember in a struct having an additional integer MaxCount member,you could always remember the number of the largest numbereditem, making it efficient to add and delete last items, andproducing a suitable stack representation. A similar considerationcould bundle a set of unordered pairs as a member inside a structhaving other integer members to remember the current numbersof the Front and Rear items of a queue, leading to an efficient queuerepresentation. Finally, instead of using structs with integermembers, you could save the MaxCount of the stack and the Front andRear item numbers of a queue inside specially configured orderedpairs, such as {–1, MaxCount} or {–1, Front} and {–2, Rear}, leading to away to represent stacks and queues efficiently using onlyunordered sets. (Here, it is assumed that you can efficently find,delete, replace, and insert an unordered pair {i,x i} in a set ofunordered pairs S , given only i as a search key. The hash tablerepesentations studied in Chapter 11 provide one possible basisfor representing such operations efficiently.)

Answers to Review Questions 7.41. The “last-in, first-out” property is crucial for the stack

implementation of the parenthesis matcher because when we scana sequence of properly nested pairs of parentheses, brackets, andbraces, and push left parentheses “(”, left brackets “[” and leftbraces “{” onto a stack, the “last-in” left-item X must match thenext non-nested right-item in the input and therefore X will be“first-out.” No, a queue with a “first-in, first-out” property would

Page 62: Standish Sol Manual Ece223

Table of Contents — continued

not work, because it would cause the parenthesis matcher torecognize improperly nested parenthesis sequences, such as“( [  { ) ]  }” in which the sequence of left-items “( [  {” gets putinto the queue, and then is removed, one-by-one, in the order: “(”to match “)” in the input, followed by “[” to match “]” in the input,followed finally by “{” to match “}” in the input.

2. Not only does Program 7.5 use a Stack ADT, it also uses only thestack operations defined in the Stack ADT’s interface withoutassuming anything about the stack’s implementation. The stackimplementation, in turn, is completely hidden inside the Stack ADTimplementation module. Thus we can switch Stack ADT modulessharing the same abstract stack operations in their interfaces, buthaving different hidden stack representations — one based on alinked stack representation and the other based on a sequentialstack representation — without affecting Program 7.5’s validity. Inthis sense, the two stack representations can be usedinterchangeably, and we have achieved the property of“substitutability of representations.”

Solutions to Selected Exercises in Exercises 7.42. Replace lines 41:45 with the following lines:

| if ( d == '(' | | d == '[' | | d == '{' ) {|| if ( Full(&ParenStack) ) {|

45 | printf("Results inconclusive. Stack overflow during processing.\n");|| return;|| } else {

50 || Push(d, &ParenStack);| }|| } else if ( d == ')' | | d == ']' | | d == '}' ) {

3. This method will not work because it will accept as valid theimproperly nested sequence of parentheses given in the followingcounterexample: “} ( ) {”. For this sequence, BraceCount will go to –1when the first brace is encountered and will later return to 0 whenthe last brace is encountered. This leaves BraceCount at 0, but theparentheses are not matched. Another counterexample is“( [ { ) ] }” which will leave all counts zero at the end, but whichis not well-formed

Page 63: Standish Sol Manual Ece223

Table of Contents — continued

Answers to Review Question 7.51. The postfix translation is: a   b  * c  –  5   /  1  3   /   ^ .

2. The infix translation is: ((x – (y + z)) / (a * b))^2.

3. Parentheses in infix notation are needed to specify the extent ofthe operands of an operator in cases where the assumedprecedence of the operators would give an incorrect result in theabsence of parentheses. For example, to specify that the sum (a +b) is the left operand of the multiplication operator in (a + b) * c,we need to use parentheses, because in the absence ofparentheses, as in writing, a + b *c , the left operand of themultiplication operator would be assumed only to be b, since theprecedence of * is assumed to be higher than the precedence of +when interpreting ordinary infix notation. In the latter case, theabsence of parentheses causes the multiplication of b and c to beperformed before the addition of a and b*c, whereas in the formercase, the addition of a and b is performed before themultiplication of (a + b ) and c . The order of operations (i.e.,mult ip l icat ion before addi t ion v e r s u s addit ion beforemultiplication) is therefore determined by the presence orabsence of parentheses. This proves that parentheses are neededin infix notation to determine the order of performance ofoperations.

By contrast, when using postfix notation, the order ofoperations is entirely determined by the left-to-right order ofthe operators in a postfix expression. Each operator performs anoperation on the most recently determined operands whenevaluating the postfix expression from left-to-right. So noparentheses are needed to determine the order of operationswhen postfix is used.

4. The topmost positions of a stack contain the most recentlydetermined operands when evaluating a postfix expression fromleft-to-right. When an operator is applied to its operands, themost recently evaluated operands are popped from the stack, andthe result of applying the operator to the operands is pushed backonto the stack. Thus, the last-in, first-out property of the stack isideally suited to the evaluation of postfix expressions. A queuewould not work properly in this application, because the already-evaluated operands of an operator would be removed from thequeue in least-recent to most-recent order, which is the wrongorder to use when attempting to apply an operator to the most-recently evaluated operands.

Page 64: Standish Sol Manual Ece223

Table of Contents — continued

Solutions to Selected Exercises in Exercises 7.5

1. The program will print out 2 as the answer. To verify that this isso, the corresponding infix-expression can be determined andthen can be evaluated as a cross-check on the correct answer:

(((6*7)–2)/5)^(1/3) =

4 2   –   2

5 ^(1/3) = 81/3 = √3

8 = 2

2. Program 7.7 can be modified to handle the errors: (1) stackoverflow, (2) division by zero, (3) malformed postfix expressionwith too many operators or operands, and (4) illegal character inpostfix expression, as shown in the following program which is anextension of Program 7.7:

/* * Exercise 7.5.2 — Interpreting a Postfix String with Error Checking – p. 270 */

#include <stdio.h>#include <stdlib.h> /* contains atof */#include <math.h> /* contains log and exp */#include <ctype.h> /* contains isdigit(d) */#include <string.h>#include "StackInterface.h"

/* Assume that the types and operations defining a stack ADT are defined *//* in "StackInterface.h". The source file "StackImplementation.c" *//* should be compiled independently and then linked with this program */

Stack EvalStack; /* where ItemType = float */ char *PostfixString;

void InterpretPostfix(void){ float LeftOperand, RightOperand, Result;

int i; /* the index of the ith character in the PostfixString */

char c; /* c = ith character of the input string */

char *s = "x"; /* s will hold a null-terminated string in which */ /* s[0] will hold c, for use in an atof conversion */

InitializeStack(&EvalStack);

for (i = 0; i < strlen(PostfixString); ++i) {

s[0] = c = PostfixString[i]; /* s[0] = c = ith character of input string */

if (isdigit(c)) { /* if c is a digit, push c's value onto stack */

if ( Full(&EvalStack) ) { printf("Stack full. Postfix Evaluation could not be completed.\n");

Page 65: Standish Sol Manual Ece223

Table of Contents — continued

return; } else { Push((float)atof(s),&EvalStack); }

} else if (c=='+' || c=='–' || c=='*' || c=='/' || c=='^') {

if ( Empty(&EvalStack) ) { printf("Malformed postfix input string. Too many operators\n"); printf("and too few operands.\n"); return; } else { Pop(&EvalStack,&RightOperand); if ( Empty(&EvalStack) ) { printf("Malformed postfix input string. Too many operators\n"); printf("and too few operands.\n"); return; } else { Pop(&EvalStack,&LeftOperand); switch (c) { /* perform the operation */

case '+': Push(LeftOperand+RightOperand,&EvalStack); break; case '–': Push(LeftOperand-RightOperand,&EvalStack); break; case '*': Push(LeftOperand*RightOperand,&EvalStack); break; case '/': if (RightOperand != 0.0) { Push(LeftOperand/RightOperand,&EvalStack); } else { printf("Attempt to divide by zero.\n"); return; } break; case '^': Push(exp(log(LeftOperand)*RightOperand),&EvalStack); break; default: break; } } }

} else {

printf("Illegal character '%c' in postfix expression.\n",c); return;

}

} /* end for */

Pop(&EvalStack,&Result); /* remove final result from stack */

if ( Empty(&EvalStack) ) { printf("Value of postfix expression = %f\n", Result); /* and print it */ } else { printf("Malformed postfix string.\n"); printf("Too many operands and not enough operators.\n"); }}

int main(void)

Page 66: Standish Sol Manual Ece223

Table of Contents — continued

{ PostfixString = (char*) malloc(20);

while (1) { printf("Please input a postfix expression or 'q' to quit: "); scanf("%s",PostfixString); if (PostfixString[0] == 'q') break; InterpretPostfix(); putchar('\n'); }}

Answers to Review Questions 7.61. If the (hidden) data representation given for an ADT can be

switched to another data representation while the program text ofthe external users of the ADT does not have to be changed, thenthe ADT has a substitutable representation.

2. This means that the Stack ADT’s notation for external users doesnot depend on its implementation in any way. If external users of aStack ADT use only a representation-independent notation, thenstack representations can be substituted for one another withouthaving to change the program text of the external stack users.

3. Programs 7.8 and 7.9 give sequential and linked stackimplementations, respectively. They both export stack operationsthat match the abstract operations in the Stack ADT interfacegiven in Figure 7.3 on p. 262. Program 7.5, which checks forbalanced parentheses, and Program 7.7, which interprets postfixexpressions, are programs that use the abstract Stack ADToperations of Figure 7.3. Consequently, it is possible to substituteeither of the sequential or linked stack implementations given inPrograms 7.8 and 7.9 into the initial declaration parts of Programs7.5 and 7.7 and to have both of the latter programs work withoutchange. This il lustrates both the substitutability of datarepresentations and good modular programming practices.

Solutions to Selected Exercises in Exercises 7.64. The following program implements the mirror image language

recognizer:

/* * Exercise 7.6.4 — Recognizing Strings in the Mirror Image Language. p. 275 */

#include <stdio.h>#include <stdlib.h>#include <string.h>#include "StackInterface.h" /* Note: ItemType == char */

/* Assume that the types and operations defining a stack ADT are defined */

Page 67: Standish Sol Manual Ece223

Table of Contents — continued

/* in "StackInterface.h". The source file "StackImplementation.c" *//* should be compiled independently and then linked with this program */

char *InputString;

Boolean Recognize(char *A) /* returns true iff the string A */{ /* is in the language */

char c;int i,L;Stack S;Boolean Match;

L = strlen(A);

/* check first to see that the string A is spelled only with the allowable *//* letters 'a', 'b', 'c', and 'm' and that its length is at least 3 */

Match = true;for (i = 0; i < L; ++i) {

if (! (A[i] == 'a' | | A[i] == 'b' | | A[i] == 'c' | | A[i] == 'm') ) {Match = false;

}}if (L < 3) Match = false;

if ( Match ) {

c = A[0]; i = 0;

InitializeStack(&S); /* initialize S to be the empty stack */

while ( (c != 'm') && (i < L) ) {Push(c,&S); /* push the character c onto the stack */i++;if (i < L) c = A[i];

}

if (c != 'm') {Match = false;

} else {while ( (i < L–1 /* not at end of string A */) && (Match /* == true */) ) {

if ( Empty(&S) ) {Match = false;

} else {Pop(&S,&c);i++;if (c != A[i]) { /* match the remaining */

Match = false; /* characters with those from the stack */}

}}

if (Match && ! Empty(&S)) Match = false;}/* end if (c != M) */

}/* end if (Match) */

return (Match);

} /* end Recognize */

Page 68: Standish Sol Manual Ece223

Table of Contents — continued

int main(void){ Boolean result;

InputString = (char*) malloc(20);

while (1) { printf("Please input a sentence to recognize or 'q' to quit: "); scanf("%s",InputString); if (InputString[0] == 'q') break; result = Recognize(InputString); printf( (result ? "true" : "false" ) ); putchar('\n'); }}

Answers to Review Questions 7.7

1. The stack space in a run-time C system is limited to a finiteportion of memory. Each time a function is called during theexecution of a C program, a (non-empty) call-frame is placed ontop of C’s run-time execution stack. When a non-terminatingrecursive function calls itself, the C run-time system attempts toallocate an unending sequence of call-frames on top of its run-time stack. This process causes all of the stack space to be usedup.

Solutions to Selected Exercises in Exercises 7.71. In a stack frame, the place reserved for returning the value of a

function call is at the bottom of the stack frame. This stack framesits just above the previous stack-top. When a stack frame for afunction call is popped off the stack, the stack pointer can be setto point to the function’s return value sitting in bottom-mostposition in the stack frame. Likewise, when evaluating anexpression, intermediate values used in the calculation of theexpression’s value can be pushed and popped on top of the stack.When the final value of the expression is calculated, it will sit ontop of the stack, awaiting its consumption by an operation of somesort. Thus, when an operand value appears on the stack, it couldhave come either from evaluating an expression or fromevaluating a function call. In either case, it sits immediately abovethe previous stack top as if it had been “pushed” onto the stack.

Answers to Review Questions 7.8

Page 69: Standish Sol Manual Ece223

Table of Contents — continued

1. The disadvantage of having just one pointer to the Front item of alinked queue representation is that to insert a new item on theRear of the queue, we have to start at the Front item and follow linksuntil we reach the last item in order to link on a new Rear item. Thistakes time O(n ) if there are n items in the queue. If we save apointer to the current Rear of the queue, we can insert a new rearitem in time O(1), instead.

2. It is possible to have such a queue representation by linking thequeue items in the one-way linked list in reverse order such thatthe R e a r item links to the next-to-last item, which links to thenext-to-next-to-last item, etc., until, finally, the F r o n t item isgiven last in the order of linkage. Even though all items in thequeue are accessible in this “reverse linked” representation, theoperation of removing the first item in the queue takes time O(n)for a queue of n items, and is therefore not as efficient as it is inmore efficient queue representations in which removal takes timeO(1).

Solutions to Selected Exercises in Exercises 7.8

1. The InitializeQueue and Insert procedure are the only ones that need tobe changed. In the InitializeQueue procedure, in Program 7.19 on page283, we need to delete line 31, which sets Q–>Rear = 0. (Also deletethe declaration of the Rear member of the Queue struct on line 10 ofProgram 7.19 on page 282.) In the Insert procedure, we need onlysubstitute the expression for the value of R e a r given by therelationship stated in the problem, Rear = (Front+Count) %

MAXQUEUESIZE.

50 | void Insert(ItemType R, Queue *Q);| {| if (Q–>Count == MAXQUEUESIZE ) {| SystemError("attempt to insert item into full Queue");| } else {

55 | Q–>Items[(Q–>Front + Q–>Count) % MAXQUEUESIZE ] = R;| ++(Q–>Count);| }| }

These changes constitute a tradeoff of time for space, because wereduced the space in each Queue record by one integer field at theexpense of spending more time to compute the location of therear of the queue, each time we needed to insert a new item.

Page 70: Standish Sol Manual Ece223

Table of Contents — continued

2. The Hint given in the problem statement gives the solution away.Some of the changes are as follows. In the Remove procedure inProgram 7.19 on page 283, replace lines 65:66 as follows:

65 | if (Q–>Front == Q–>Rear) {| SystemError("attempt to remove item from empty Queue");

Then in the Insert procedure, replace lines 52:53 as follows:

| if (Q–>Front == ((Q–>Rear+1) % MAXQUEUESIZE ) {| SystemError("attempt to insert item into a full Queue");

Then delete the Count field from the declaration of the Queue recordon line 8 and remove all operations on the Count field in the queuefunctions. The definitions of Emp ty (&Q) and Ful l (&Q) have to bemodified in a fashion similar to that given immediately above forlines 52:53 and 65:66.

3. Just remove the items from the front of the queue one-by-one andpush them onto the stack until the queue becomes empty. Thenpop the items off the stack one-by-one and insert them into thequeue until the stack becomes empty, as follows:

| void ReverseQueue(Queue *Q)| {| Stack *S;| ItemType F;

5 || InitializeStack(&S);|| while ( ! Empty(&Q) ) {| Remove(&Q, &F);

10 | Push(F, &S);| }|| while ( ! Empty(&S) ) {| Pop(&S, &F);

15 | Insert(F, &Q);| }| }

4. The queue is defined to be empty if and only if Rear == NULL. If Rear

is not NULL, then the queue is non-empty and, by convention, we letthe queue node struct for the rear node link to the node for thefront item of the queue, rather than having a NULL link field in therear queue node, as is normal in the one-way-linked queuerepresentation. To insert an item into the rear of the queue, weinsert a new queue node after the current rear item and before thefront item. To remove an item from a non-empty queue, we willhave to consider two cases: (1) if there is only one item in thequeue, we remove the only item and set Rear = NULL; (2) If there ismore than one item, we follow links around the circular list until

Page 71: Standish Sol Manual Ece223

Table of Contents — continued

finding the predecessor node P for the rear node, i.e., the nodewhose Link field points to Rear and then we set Rear = P, remove theformer Rear node, and set the link of P to point to the Front node thatthe link in the Rear node used to point to.

Here is a program that will remove an item node from the queue:

| void Remove(Queue *Q, ItemType *F)| {| QueueNode *Prev;|

5 | Prev = Q–>Rear; /* Prev will eventually be set to */| /* the predecessor of Q–>Rear */|| if ( Prev == NULL ) { /* if the Queue is empty, report error */| SystemError("attempt to remove item from empty queue");

10 | } else if (Prev–>Link == Prev) { /* if there is only one node in Q */| /* free it and set Q–>Rear to NULL */| *F = Prev–>Item;| Q–>Rear = NULL;| free(Prev);

15 || } else { /* otherwise */| while (Prev–>Link != Q–>Rear) { /* find the predecessor */| Prev = Prev–>Link; /* of Q–>Rear */| }

20 | *F = Q–>Rear–>Item;| Prev–>Link = Q–>Rear–>Link; /* link Prev to front of queue */| free(Q–>Rear); /* free old Q–>Rear */| Q–>Rear = Prev; /* and set new Q–>Rear to Prev */| }

25 || }

5. Answer not given.

6. This representation requires that the whole queue be shifted downwhenever we do a R e m o v e operation. This increases the timecomplexity of the R e m o v e operation from O(1) to O(n ) and istherefore inefficient.

Answers to Review Questions 7.91. Queues are useful for regulating the flow of tasks within a system,

especially when tasks must be processed by several kinds ofservers. Queues are commonly used in operating systems for thispurpose. Additionally, queues are used to synchronize theexchange of data between two concurrent processes running atdifferent speeds. Print buffers and printer spoolers offer twocommon examples of such synchronization. Queues are also usefulin setting up simulations and models of systems in which clientsarrive and are given service.

Page 72: Standish Sol Manual Ece223

Table of Contents — continued

2. When we use a queuing system simulation to model an actualsystem we wish to study, we try to model each queue in the real-world system with a queue in the simulation model. We try tomodel the arrival rate of clients entering queues to await serviceat various servers. We try to model accurately the service timestaken by clients when being served by servers and we try tomeasure how well the overall system performs. For example, wemay be interested in measuring: (a) the average waiting time toobtain service, (b) the variance in the waiting times, (c) theaverage length of the queues and (d) the throughput of the system.All these output measures can be determined and may bedescriptively accurate if the system is modeled correctly.

Solution to Selected Exercises 7.91. Answer not given.

Page 73: Standish Sol Manual Ece223

Table of Contents — continued

Answers to Review Questions 8.21. A list is a sequence of items. Some operations usually permitted

on lists are selecting the ith item; insertions, deletions andreplacements of items in arbitrary places; finding the length of alist; and determining whether or not a list is empty.

2. If a packed array is used to represent a list then when an itemneeds to be added to the list or deleted from it, all the itemsbeyond the point of insertion or deletion must be shifted in orderto maintain the list’s contiguous sequential representation.Another potential difficulty relates to the fact that the array sizemust be determined in advance of storing list items. If lists areused in circumstances in which we need to handle unpredictablegrowth in the amount of data, it is inconvenient to have to definethe array size before knowing how big the array might need to beto store potential future list items. On the other hand, if weinitially define arrays that are very large and use only a portion ofthem, we risk wasting a considerable amount of space. Anotherpotential inefficiency relates to the need to pack and unpack listitems if several list items are stored in packed form in a singlearray item A[i].

3. It would be advantageous to use a sequential array to represent alist if the application requires frequent access to the ith item ofthe list as compared to insertions or deletions and if a limit on thelist size is known in advance and is never violated during therunning of the program.

4. A circular linked list has the advantage that any node on it isaccessible from any other node. Hence, it could be used when eachnode has to be reached from some other node in the list. Forexample, in circular lists containing two or more items, it isalways possible to delete the node before a given node N startingonly with a pointer to node N. (However, a note of caution is inorder here: If an external pointer is being maintained that pointsto one of the nodes on the circular list, it is important to be ableto update this external pointer when it points to the node beingdeleted.)

Solutions to Selected Exercises in Exercises 8.21. Consider an arbitrary item, xi. We have to shift left all items after

item xi up until the end of the array, n. Hence, the number of leftshifts to delete the ith item is S(i) = (n – i). We now compute the

Page 74: Standish Sol Manual Ece223

Table of Contents — continued

total number of left shifts, assuming that we delete items xi in allpossible positions i of the array.

∑(1≤i≤n) S(i) =

= ∑(1≤i≤n) (n – i)

= ∑(1≤i≤n) n – ∑(1≤i≤n) i

= n2 – n(n+1)/2

= n(n – 1)/2

Now, to average over all n items of the array, we divide the lattertotal by n, getting:

(1/n)*n(n – 1)/2 = (n– 1)/2

Therefore, the average time for deletion in such an array is O(n).

2. By an analysis similar to that for Exercise 1, the average timerequired for insertion is O(n). The analysis goes as follows: If weinsert a new item after the last, or n th, item, we have to shift 0items to the right. If we insert a new item after the (n – 1)st item,we have to shift 1 item to the right. In general, if we have to inserta new item after the ith item, we have to shift (n – i) items to theright. Finally, if we have to insert a new item before the first item,we have to shift n items to the right. The total time to shift itemsfor insertions in all (n + 1) possible positions (from before thefirst to after the last) is a sum of the form 0 + 1 + 2 + ... + n =n(n+1)/2. Dividing by n + 1 yields n/2 shifts on the average. Hence,the average number of shifts is O(n ). Since the shifting timedominates the time required to do insertion, the average insertionoperation takes time O(n).

3. Some arguments supporting the results in Table 8.5 are as follows:

(a) Finding the length of L

Sequential Rep.: O(1). Since we explicitly store the count of thenumber of items in the list all we have to do is access thevalue of the count to get the list length.

Linked Rep.: O(n). We have to traverse the entire list to countthe number of linked nodes in it.

(b) Inserting a new first item

Sequential Rep.: O(n). To insert a new first item we have to shiftall other elements in the array one space to the right.

Linked Rep.: O(1). All we have to do is link the new first item tothe previous first item and then change the pointer tothe old first list item to point to the new first item.

(c) Deleting the last item

Page 75: Standish Sol Manual Ece223

Table of Contents — continued

Sequential Rep.: O(1). We need only decrement the counter whichtells us the number of items in the list.

Linked Rep.: O(n). We have to traverse the entire list to find thepredecessor of the last item. Then we haveto delete the last item and set its predecessor’s l i n k t oNULL. The dominant time is the traversal time O(n).

(d) Replacing the ith item

Sequential Rep.: O(1). Replace the ith item directly with A[i] = R.

Linked Rep.: O(n ). Access the ith node, starting from thebeginning of the list and then replace its item. Theaverage must be taken over all i from 1 to n. T h e t o t a lnumber of pointers to follow to access all i items, starting with a

pointer to the first node, is 1 + 2 + 3 + ... + n = n*(n + 1)/2. Sothe average is (n+1)/2 which is O(n).

(e) Deleting the ith item

Sequential Rep.: O(n). You have to shift (n – 1) items leftwardafter deleting the first item. You have toshift (n – i) items leftward after deleting the ith i tem.Consequently, the total number of left shifts after deleting each ofthe n items is (n – 1) + (n – 2) + ... + 1 + 0 = n*(n – 1)/2. So theaverage number of shifts is n*(n – 1)/2 whichis O(n).

Linked Rep.: O(n). To access the predecessor of the ith node andlink it to the successor of the it h node requiresfollowing i pointers, starting with a pointer to the firstnode of the linked list. The average of i for i in the

range 1 ≤ i ≤ n is O(n).

4. Each node in the linked list requires p bytes for the pointer and qbytes for the item. Hence the space requirement for a linked listof n nodes is n*(p + q). An array of size MaxSize requires q bytesfor each item and there are up to MaxSize number of items. Hencethe space preallocated for the array is q*MaxSize. Now we set thespace requirement for the linked list of length n equal to thespace requirement for the array and solve for n to get the resultgiven by Equation (8.1) on p. 302, as follows:

n*(p + q) = q*MaxSize

n = q

(p   +   q ) * MaxSize

5. Assuming that queue nodes containing the queue items are linkedto one another in the order of front queue items to rear queueitems, the Queue ADT would be represented best by DaiseeChayne’s design. The reason for this choice is that the Front of the

Page 76: Standish Sol Manual Ece223

Table of Contents — continued

queue can be easily accessed from the Rea r of the queue byreferring to the item pointed to by the Link field of the node thatthe Rear pointer references. Alf Witt’s design is not efficientbecause the process of insertion would require traversing theentire length of the queue before coming to the Rear pointer toperform the insertion. You might think that you could make Witt’sdesign work equally as well as Chayne’s by linking the nodes forthe queue items in the order of rear queue items to front queueitems, but such is not the case. With such reverse linking, you caneasily insert a new rear item, but removing the front itemrequires traversing nodes on the circular list in the order of rear-to-front items to find the predecessor of the front node, in orderto link it to the rear node. Hence, Chayne’s representation issuperior no matter which linking direction is used.

6. The implementations of the List ADT operations on the one-waycircular linked-list representation are given in the followingcomplete C program:

/**** Exercise 8.2.6, page 305 –– Circular List Operations***/

#include <stdio.h>#include <stdlib.h>

typedef int ItemType;

typedef struct NodeTag {ItemType Item;struct NodeTag *Link;

} ListNode;

void Initialize (ListNode **L) /* Initialize L to be the empty list. */{

*L = NULL;}

Boolean Empty(ListNode *L){

return (L == NULL);}

int ListLength(ListNode *L){

int i;ListNode *Temp;

if (L == NULL) { /* if the list is empty */return 0; /* its length is zero */

} else { /* otherwise, traverse list until */

Page 77: Standish Sol Manual Ece223

Table of Contents — continued

i = 1; /* the last node is reached */Temp = L;while (Temp–>Link != L) {

i++;Temp = Temp–>Link;

}return i; /* return the length of the list */

}}

ListNode *Select(int i,ListNode *L){

int j;ListNode *Temp;

if (L == NULL) {return NULL;

} else {j = 1;Temp = L;while ((i != j) && (Temp–>Link != L)) { /* find node */

Temp = Temp–>Link; /* you are looking for */j++; /* by advancing i nodes in the list */

}if (i == j) {

return Temp;} else {

return NULL;}

}}

void Replace (int i, ItemType Y, ListNode *L){

ListNode *Temp;

Temp = Select(i,L);if (Temp != NULL) Temp–>Item = Y;

}

void DeleteFirst (ListNode **L){

ListNode *First, *Last;

First = *L;

if (First != NULL) { /* if list is non–empty */if (First–>Link == First) { /* if list has only one node */

free(First); /* recycle storage for the node */*L = NULL; /* and set the list pointer in L to NULL */

} else { /* list had more than one node */Last = First;while (Last–>Link != First) { /* locate last node */

Last = Last–>Link;}*L = Last–>Link = First–>Link; /* link around first and reset *L */free(First); /* recycle storage for deleted node */

}}

Page 78: Standish Sol Manual Ece223

Table of Contents — continued

}

void Delete(int i, ListNode **L){

ListNode *Predecessor, *N;

if (i == 1) {DeleteFirst(L);

} else {Predecessor = Select(i–1,*L); /* find predecessor of node N to delete */if ( (Predecessor != NULL) && ( (N = Predecessor–>Link) != *L) ) {

Predecessor–>Link = N–>Link; /* link around node to delete */free(N); /* recycle storage for deleted node */

}}

}

void InsertFirst(ItemType Y, ListNode **L){

ListNode *NewNode,*Last;

NewNode= (ListNode *) malloc (sizeof(ListNode));NewNode–>Item = Y;

if (*L == NULL) {*L = NewNode;NewNode–>Link = NewNode;

} else {Last = *L;while (Last–>Link != *L) {

Last = Last–>Link;}Last–>Link = NewNode;NewNode–>Link = *L;*L = NewNode;

}}

void Insert(int i,ItemType Y, ListNode **L){

ListNode *N, *Temp;

if (i == 1) {InsertFirst(Y,L);

} else {N = Select(i–1,*L);if (N != NULL) {

Temp = (ListNode *) malloc(sizeof(ListNode));Temp–>Item = Y;Temp–>Link = N–>Link;N–>Link = Temp;

}}

}

void PrintList(ListNode *L){

Page 79: Standish Sol Manual Ece223

Table of Contents — continued

ListNode *Temp;

printf("(");

if (L != NULL) {Temp = L;do {

printf("%d",Temp–>Item);if (Temp–>Link != L) printf(","); /* print commas between list items */Temp = Temp–>Link;

} while (Temp != L);}

printf(")\n");}

int main(void){

int i,k;ListNode *L, *N;

/* test initialization */Initialize(&L);

/* test empty function */if (Empty(L)) {

printf("it was empty\n");} else {

printf("It was non–empty\n");}

/* test length function */k = ListLength(L);PrintList(L);printf("list length was == %d\n",k);

for (i=5; i>=1; i––) {InsertFirst(i,&L);/*

k = ListLength(L);PrintList(L);printf("list length was == %d\n",k);

*/}PrintList(L);

/* test replacement */for (i=0; i<=6; i++) {

Replace(i,11*i,L);}PrintList(L);

/* test selection */for (i=1; i<=5; i++){

N = Select(i,L);if (N != NULL) printf(" the %dth item was == %d\n",i,N–>Item);

}

/* test deletion */Delete(5,&L);PrintList(L);

Page 80: Standish Sol Manual Ece223

Table of Contents — continued

Delete(1,&L);PrintList(L);Delete(2,&L);PrintList(L);Delete(2,&L);PrintList(L);Delete(1,&L);PrintList(L);

/* test insertion */

for (i=1; i<6; i++) {Insert(i,i,&L);

}PrintList(L);

for (i=0; i<7; i++) {Insert(2*i+1,11*i,&L);

}PrintList(L);

}/* end main */

7. Move the contents of the I tem field of N ’s successor into N ’s I tem

field. Save a pointer P to N’s successor. Set N’s Link field to containthe pointer in P ’s Link field. Finally, free the storage for the nodepointed to by P. A C code fragment to perform these actions is asfollows:

| ListNode *P;|| N–>Item = N–>Link–>Item;| P = N–>Link;

5 | N–>Link = P–>Link;| free(P);

Answers to Review Question 8.31. A generalized list is a list in which the individual list items are

permitted to be sublists, unlike simple linear lists in whichindividual items are not lists.

2. We can use C unions to define the structure of generalized listnodes. Here is an example :

#define TRUE 1#define FALSE 0

typedef int ItemType;

typedef struct GenListTag {struct GenListTag *Link;short Atom;union SubNodeTag {

ItemType Item;struct GenListTag *SubList;

Page 81: Standish Sol Manual Ece223

Table of Contents — continued

} SubNode;} GenListNode;

Then, if the Atom field of a node contains the constant value TRUE,the node’s Item field contains an ordinary list item. But if the A t o m

field contains the constant value FALSE, then the node’s SubList fieldcontains a pointer to the first node of a generalized sublist.

3. If some pointers in a generalized list G formed a cycle (i.e., a pathcomposed of pointers which begins at some node and travels backto itself), then Program 8.12 would get into an endless loop.

Solutions to Exercises 8.3

No solutions are given in this section.

Answers to Review Questions 8.41. In a typeless language, variables can take values of any type, such

as integers, floating point numbers, characters, strings or lists.This is not possible in a hard-typed language where the types ofvariables have to be declared before the variables can be used andwhere variables may be assigned values only if the value types tobe assigned agree with (or may be implicitly converted to ortypecast to) the types of the variables being assigned.

2. Typeless languages permit the construction of generalized listscontaining many different types of atomic items. This provides ahighly flexible kind of data structure which can meet therequirements of applications having highly unpredictabledemands for supporting representations. A hard-typed languagemight waste space to provide the same kind of flexibility andgenerality and its programs might require extensive case anaylsisbased on type switching.

3. The price paid is that interpreted type-switching code fortypeless languages runs much slower than compiled code for hard-typed languages because type switching must be done at run-timefor typeless languages, whereas no type switching code need beexecuted at run time for programs compiled from a program in ahard-typed language.

4. In symbolic algebraic manipulation systems, certain derivationsoften involve long chains of intermediate steps using expressionsthat can swell to unpredictably large sizes. This leads to a

Page 82: Standish Sol Manual Ece223

Table of Contents — continued

phenomenon called intermediate expression swell in algebraicmanipulation systems.

Solutions to Exercises 8.4

No solutions are given for this section.

Answers to Review Questions 8.51. The character constant '\0' represents the character with the value

zero and is sometimes called “the null byte.” A C string is acharacter array consisting of a sequence of characterrs endingwith the null byte. In C, a null string is represented by a characterarray containing a single null byte whose value is zero. The emptystring in C programs is denoted by two consecutive double quotes," ".

2. In C, character strings are represented by contiguous sequencesof characters having a special null character which terminates thesequence. In C, the problem of string overflow can be handled byallocating new blocks of characters big enough to accommodatethe string’s growth to any new size, provided the underlyingmemory allocation system can find space. Pascal strings are ofpredetermined, bounded length, so overflows cannot beaccommodated conveniently. General Pascal strings consume 256bytes of space for strings of between 0 and 255 characters inlength. Unused space inside the packed character arrays holdingthese Pascal string representations is wasted. Because C stringsare not of bounded length, no space is wasted. However, to find thelength of a C string, we must scan the characters in the string untilreaching the terminating null byte at the end of the string. This isan O(n) operation for a string of n characters, whereas Pascal’slength operation is O(1), since it looks up the length byte in thezeroth position of the packed character array representing thestring.

3. Text files can be represented on external storage media as linkedblocks in which each block contains a packed array of charactersholding a portion of the text. Each of these linked blocks can bestamped with: (1) a unique file identifier, (2) the date and time ofthe text file’s creation, and (3) a block sequence number. Thisinformation can be used to recreate the text file from the rawundamaged text blocks in the event that the disk’s directory is

Page 83: Standish Sol Manual Ece223

Table of Contents — continued

damaged or destroyed, or in the event of an unintended, accidentaldeletion of the text file from the directory.

4. Text files can be represented in main memory by coalescing theircharacters into a single large block containing the sequence ofcharacters in the text. A separate array of line starts can becreated which is useful for rapid display and scrolling of the textby a word processor. Sequences of control character codes, whichare invisible to the word processor user because they are notprinted on the screen, can be interspersed in the text’s charactercode sequence, and can designate format features of theunderlying text (such as page breaks, special character fonts orsizes, tab alignments, and so forth).

Solutions to Selected Exercises in Exercises 8.5

1. The strlen function:

/* strlen is a standard C function usually explained in C texts such as *//* e.g., The C Programming Language, Second Edition, by Kernighan *//* and Ritchie (known as K & R), Prentice-Hall, 1988, page 39 */

| int strlen(char S[ ])| {|| int i;

5 || i = 0;| while (S[i] != '\0' ) {| ++i;| }

10 || return i;|| }

2. The strstr(S, T) function (return a pointer to the first occurrence ofstring S in string T, or NULL if no occurrence is present):

| char *strstr(char *S, char *T) /* return pointer to first occurrence of S in T */| {| char *p, *q, *r;|

5 | p = S;| r = T;|| while (1) {|

10 | q = r;|| while ( (*p != '\0') && ((*r != '\0') && (*p++ == *r++)) ) {| ;| }

Page 84: Standish Sol Manual Ece223

Table of Contents — continued

15 || if (*p == '\0') {| return q;| } else if (*r == '\0') {| return NULL;

20 | } else {| p = S;| r++;| }| }

25 | }|

3. The strcpy function (from K&R, p. 105.):

| void strcpy(char *S, char *T )| {| while ( ( *S++ = *T++) != '\0')| ;| }

4. The strspn(S, T) function (return length of prefix of S with charactersin T):

|| /* first define the auxiliary function Member(c,T) which returns */| /* true iff c is a non-null character that found in string T */|

5 | Boolean Member(char c, char *T)| {| if (c == '\0') {| return 0;| } else {

10 | while ((*T != '\0') && (*T != c)) {| T++;| }| return ( *T != '\0' );| }

15 | }|| int strspn(char *S, char *T) /* return length of prefix of S with chars in T */| {| int i;

20 || i = 0;|| while (Member(S[i],T)) { /* Member returns false if S[i] == '\0' */| i++;

25 | }|| return i;| }

Answers to Review Questions 8.61. Static storage allocation takes place before a program is

executed and involves setting up storage areas whose size and

Page 85: Standish Sol Manual Ece223

Table of Contents — continued

arrangement can be calculated from declarations in the text of theprogram before the program is run. Dynamic storage allocationinvolves allocating portions of memory in various sizes andshapes during the execution of the program. In dynamic allocationwe may be able to calculate the sizes and shapes needed onlywhile the program is being run and not beforehand. Three examplesof dynamic allocation are: (1) Stack-based dynamic allocation usedto support subroutine calls and expression evaluation, (2)Organizing memory into an available space list in order to performdynamic allocation of list nodes on demand at run-time, and (3)Organizing memory into a heap which can be used to allocateblocks of differing sizes on demand during program execution.

2. There are three phases in the marking and gathering method forgarbage collection. In the initialization phase all list nodes aremarked as free. In the marking phase all list nodes in current useare marked as reserved. In the gathering phase all free list nodesare linked into a new available space list.

3. A heap is a zone of memory organized to support dynamic memoryallocation of blocks of storage of different sizes. The heap isorganized into a collection of reserved blocks that are in use bythe running program, and a collection of free blocks available forallocation to satisfy future allocation requests. The free blocksmay be linked into lists, rings, trees, or other forms of dataorganization, in order to promote the efficiency of the process ofsearching for a free block to satisfy an allocation request.

4. Two methods for allocating a block of size n in the heap are thefirst-f i t method and the best - f i t method. The first-fit methodscans a two-way linked ring of free blocks and allocates the firstblock big enough to satisfy the request. The best-fit method scansthe entire ring of free blocks to find the block that is the tightestpossible fit satisfying the request, where the tightest possible fitis the one having least excess size beyond the size requested.

5. Fragmentation occurs during the use of the first-fit and best-fitpolicies when free blocks are split into smaller blocks. Coalescingis a method used to counteract the tendency towardsfragmentation. When a block is liberated and can be returned tothe ring of free blocks, it is possible to coalesce it withneighboring blocks that are also free, in order to form larger freeblocks.

6. Compacting is a strategy that can be used for storage reclamationwhen allocation failure occurs in a heap. When a heap is compacted,all reserved blocks are moved to one end of the heap, allowing allfree blocks to be moved to the opposite end and to be coalesced

Page 86: Standish Sol Manual Ece223

Table of Contents — continued

into one large free block. Handles are used to make heapcompacting easier. A handle is a pointer to a pointer. Eachreserved block in a heap is referenced by a master pointer in aspecial master pointer region. A handle to a block is a pointer tothe block’s master pointer. When a block is moved during heapcompacting, its master pointer is updated to point to the block’snew location. References to the block using double dereferencingof the block’s handle, are unaffected by the act of moving a blockand updating its master pointer.

7. In a reference count technique, each node contains a number,called its reference count, which equals the number of externalpointers which point to the node. Each time an external pointer toa node is destroyed, the node’s reference count is decreased byone. When the node’s reference count becomes zero, its storagecan be returned to the pool of available space. Since this can bedone incrementally, long pauses for the execution of multipassstorage reclamation algorithms can be avoided.

Solutions to Exercises 8.61. To make the coalescing policy work efficiently, we need to be able

to inspect the neighboring blocks above and below a given blockthat is being freed so that it may be joined together with either orboth neighboring blocks if either or both is free. (Here, when weuse the words “top” and “bottom” we are referring to memoryaddress order, where “up” is the direction of decreasing memoryaddresses, and “down” is the direction of increasing memoryaddresses.) Suppose we are trying to free a given block, B. If wewere to store every block’s size and mark bit only at its top andnot at its bottom, then when we are trying to look at the blockimmediately above B to see if it is free and available forcoalescing, we would have no information to use to determine itstop neighbor’s size and status (free or reserved). By storing thesize and mark bit of a block at its bottom boundary as well as atits top boundary, we can look at the address immediately beforeB’s starting address (which is the location of B’s top neighbor’sbottom boundary) to find the size and mark bit of B’s top neighbor.This permits us to coalesce B with its top neighbor in the eventthat B’s top neighbor is found to be free.

2. Consider any collection of nodes having links that form a cycle(i.e., a path formed from pointers that starts at some node andreturns to that node). The reference counts in each node in a cyclecan never become zero, even though there are no external

Page 87: Standish Sol Manual Ece223

Table of Contents — continued

pointers pointing to any node in the collection. Consequently, areference-count based incremental storage reclamation techniquecan never return any of the nodes in such a cycle to free storage.Islands of nodes linked by pointer cycles can thus form and willnever be garbage collected using a reference-count technique.

Page 88: Standish Sol Manual Ece223

Table of Contents — continued

Answers to Review Questions 9.2

1. (a) R, T

(b) X

(c) X, Y, Z

(d) T

2. The root node in a tree is the only node having no ancestors.

3. A leaf.

4. There is one and only one path from the root node R in a tree toany given node N that is a descendant of R.

Solutions to Selected Exercises in Exercises 9.2

1. In a tree, the number of nodes n is always 1 greater than thenumber of edges e. Thus, n = e + 1. This relationship must hold truein all trees because each node, except the root, sits at the bottomof exactly one edge. Thus the number of edges equals the thenumber of non-root nodes. Adding the root node, the number ofnodes equals one plus the number of edges.

2. A proof can be constructed using the idea that the number of edgetops equals the number of edge bottoms. Thus, 2*I = # edge tops =# edge bottoms = L + I – 1. This implies 2*I = L + I – 1, from which,by subtracting I from both sides and adding one to both sides, weget, L = I + 1.

Answers to Review Questions 9.31. A binary tree is either the empty tree or else is a node that has

left and right subtrees that are binary trees.

2. An extended binary tree is a binary tree having its empty binarysubtrees explicitly represented by square symbols.

3. A complete binary tree is a binary tree with leaves either on asingle level or on two adjacent levels such that the leaves on thebottommost level are placed as far left as possible.

4. Yes, a single node with empty left and right subtrees is a binarytree whose single node is both a root and a leaf.

Page 89: Standish Sol Manual Ece223

Table of Contents — continued

Solutions to Selected Exercises in Exercises 9.31. The number of empty binary trees (squares) in an extended binary

tree is one greater than the number of internal (non-empty) nodes(circles) because such a tree is a full binary tree (see the solutionto Ex. 9.2.2).

2. At each level l, there are 2l nodes, so the total number of nodes is:

∑ (0≤l≤n) 2l = 20 + 21 + 22 +...+ 2n = 2n+1 – 1 .

[See the solutions to Exercises A.2.1 and A.2.2 in the MathReference Appendix, or apply Eq. A.8 in the Appendix to the sumabove to determine the solution.]

Answers to Review Question 9.4

1. If we number the nodes of the complete binary tree in level orderas shown in Figure 9.5 on p. 344, we can represent the tree nodesin an array A in which the ith node in level order is stored in arrayposition A[ i] . Table 9.7 on p. 345 defines the mathematicalproperties of this representation, which is called the contiguoussequential representation of the complete binary tree.

2. In the contiguous sequential representation, the parent of A[i] isA[i / 2]. The root, A[1], has no parent.

3. In the contiguous sequential representation, the left child of A[i] isA [2*i]. All nodes with 2 i > n have no left children, and each suchnode is a leaf.

4. In the contiguous sequential representation, the right child of A[i]is A[2*i+1 ]. Any node with 2*i+1 > n has no right child, but it mayhave a left child (when 2*i = n).

Solutions to Selected Exercises in Exercises 9.41. If A[i] is a leaf, it has no left child A[2*i]. But if 2*i ≤ n then the left

child A[2*i] would have existed. Consequently we must have 2*i >n. On the other hand, suppose 2*i > n is given. Then it is also truethat 2*i+1 > n. Hence both the left child A[2*i] and the right childA[2*i+1] of node A[i] do not exist. Because A[i] has no children it isa leaf.

2. The level number l of the level containing the n th node in acomplete binary tree is l = lg n . [Strictly speaking, the numberof levels in a complete binary tree T is one greater than the levelnumber l of the bottom row of T because the levels are numberedstarting at 0 for the topmost level containing the root. Thus thenumber of levels is 1 + lg n .]

Page 90: Standish Sol Manual Ece223

Table of Contents — continued

3. A [i] resides on level l = lg i .

Answers to Review Question 9.51. A heap is a complete binary tree with values stored in its nodes

such that no child has a value greater than the value of its parent.

2. A heap can be used to represent a priority queue by implementingappropriate removal and insertion operations. The r e m o v a loperation removes the value from the root of the heap (which isthe largest value in the heap — and thus the highest priority valuein the priority queue) after which the tree with the missing rootvalue must be reheapified by placing the value of the last leafnode in level order into the root and repeatedly exchanging itdownward along a path from the root toward the leaves with thelarger of any children node values exceeding its own value. Theinsertion operation adds a new value by inserting it in a new lastleaf in level order and repeatedly exchanging the new value withany smaller parent value upward along a path from the new leaftoward the root.

3. Starting with the last internal node in level order, which is nodeA[ n/2 ] , and proceeding to process all internal nodes fromA[ n/2 ] backwards toward the root node A[1 ] in reverse levelorder, take the value of each such internal node and repeatedlyexchange it downward along a path toward the leaves with thelarger of any child values exceeding its own value. The entireprocess takes time O(n) for a heap of n nodes.

4. Removing and inserting heap items are operations each takingO(log n) for a heap of n nodes.

Solutions to Selected Exercises in Exercises 9.51. Suppose there exists a heap H with root R having a node N ≠ R

containing a value larger than the value in any other node of H.Then let P be the parent node of node N. (Node N must have aparent because the root of a tree is the only node with no parent,and N is not the root). Then N’s value is larger than P’s value whichviolates the heap property that no child can have a value largerthan the value of its parent. Consequently, there exists no heapwith a non-root node having a value larger than the value of anyother node. Therefore, the largest value in a heap always residesat the root.

2. n/2 nodes are internal nodes and n/2 nodes are leaf nodes.

3. The solution is given by the Heapify function in the followingsource program:

Page 91: Standish Sol Manual Ece223

Table of Contents — continued

/* * Solution to Ex 9.5.3 –– also does the example in the book * –– by converting Fig. 9.17 into Fig. 9.8 */

#include <stdio.h>#define MAXCOUNT 10

typedef int HeapItemType; /* for now */typedef int HeapNodeType;

typedef struct {int Count;HeapItemType ItemArray[MAXCOUNT+1];

} Heap;

/* ---------------------------------------------------------------------- */

void BuildInitialNonHeap(Heap *H){

int i;HeapItemType InitializerArray[ ] = {0,2,4,5,7,3,10,8,1,9,6};

H–>Count = MAXCOUNT;for (i=1; i<=MAXCOUNT; i++) H–>ItemArray[i] = InitializerArray[i];

}

/* ---------------------------------------------------------------------- */

void Reheapify(Heap *H, HeapNodeType N){

HeapNodeType M;HeapItemType V1,V2, temp;Boolean Finished;

/* Let V1 refer to N's value */

V1 = H–>ItemArray[N];Finished = (2*N > MAXCOUNT); /* Finished = true iff */

/* node N has no children */

while (!Finished) { /* let M be the child of node N having the larger value V2 */

M = 2*N; /* let M be the left child of N */if (M < MAXCOUNT) { /* if a right child of node N exists, then */

if (H–>ItemArray[M+1] > H–>ItemArray[M]) M++; /* if the */} /* right child’s value is larger than the left child's value */V2 = H–>ItemArray[M]; /* then let M be the right child of N */

if (V1 >= V2) {Finished = true;

} else {/* exchange the values in nodes N and M */

temp = H–>ItemArray[N];H–>ItemArray[N] = H–>ItemArray[M];H–>ItemArray[M]=temp;

/* Let N refer to node M and let V1 refer to N's value */N = M;V1 = H–>ItemArray[N];Finished = (2*N > MAXCOUNT); /* true iff node */

/* N has no children */

Page 92: Standish Sol Manual Ece223

Table of Contents — continued

}}

}

/* ---------------------------------------------------------------------- */

void Heapify(Heap *H){

int i;

for (i = H–>Count/2; i>=1; ––i) {Reheapify(H,i); /* reheapify H starting at node i */

}

}

/* ---------------------------------------------------------------------- */

void PrintHeap(Heap *H){

int i;

for (i=1; i<=H–>Count; ++i) {printf("%2d–%2d; ",i,H–>ItemArray[i]);

}printf("\n");

}

/* ---------------------------------------------------------------------- */

int main(void){

Heap H;

BuildInitialNonHeap(&H);PrintHeap(&H);Heapify(&H);PrintHeap(&H);

}/* main program */

4. This exercise is solved in the Math Reference appendix on pages718-720, yielding Eq. A.28, from which it can be shown that ∑(0≤i≤ l–

1) i/2 i = 2 – (l+1 ) /2 l– 1 , from which it follows that ∑(0≤ i≤ l–1 ) i/2 i < 2for any l ≥ 0.

Answers to Review Questions 9.61. PreOrder, InOrder, and PostOrder.

2. The three traversal orders are defined recursively as follows:

PreOrder: Visit the root. Traverse the left subtree in PreOrder.Traverse the right subtree in PreOrder.

Page 93: Standish Sol Manual Ece223

Table of Contents — continued

InOrder : Traverse the left subtree in InOrder. Visit the root.Traverse the right subtree in InOrder.

PostOrder: Traverse the left subtree in PostOrder. Traverse theright subtree in PostOrder. Visit the root.

3. No. LevelOrder visits all siblings and cousins of a node N on thesame level before visiting any children of N, whereas PreOrder,InOrder, and PostOrder visit all descendants of N before visitingany siblings or cousins of N on the same level as N.

4. Stacks and queues can hold postponed obligations to visitsubtrees of a tree by storing pointers to the subtrees as stack orqueue items. Suppose we visit a node N, then insert pointers to theleft and right subtrees of N on the rear of a queue, and thenremove a pointer from the front of the queue to obtain a pointerto the next node to visit. The result will be a LevelOrder traversalof the tree rooted at N. If we visit a node followed by pushingpointers to the right and left subtrees of N on a stack, we canimplement a PreOrder traversal non-recursively.

Solutions to Selected Exercises in Exercises 9.6

1. PreOrder Traversal : R S X Y Z T U V W

InOrder Traversal : X S Y Z R U T W V

PostOrder Traversal : X Z Y S U W V T R

LevelOrder Traversal : R S T X Y U V Z W

2. The tree is:/

a b^

b 2

+

^

a 2

*

b*

2 a

3. The solutions to Ex. 9.6.3 and Ex. 9.6.4 are given in the followingprogram:

| /*| * Solutions to Ex 9.6.3 and 9.6.4

Page 94: Standish Sol Manual Ece223

Table of Contents — continued

| */ |

5 || #include <stdio.h>| #include <stdlib.h>| #include <string.h>|

10 || typedef struct NodeTag {| char Symbol;| struct NodeTag *LLink;| struct NodeTag *RLink;

15 | } Node;|| /* Assume typedef enum { false, true} Boolean; has been given */|| extern void PrintParens3(Node *T);

20 | extern void PrintParens2(Node *T, char op);|| /* ---------------------------------------------------------------------- */|| Boolean IsOperator(char c)

25 | {| switch (c) {| case '+':| case '–':| case '*':

30 | case '/':| case '^':| return true;| default:| return false;

35 | }| }|| /* ---------------------------------------------------------------------- */|

40 | Node *PrefixStringToTree(char **S)| {| char c;| Node *N;|

45 | c = *(*S)++; /* extract next character from string S */| /* and postincrement char pointer (*S) */| N = (Node *) malloc(sizeof(Node)); /* create a new node, N */| N–>Symbol = c;|

50 | if (IsOperator(c)) {| N–>LLink = PrefixStringToTree(S);| N–>RLink = PrefixStringToTree(S);| } else {| N–>LLink = NULL;

55 | N–>RLink = NULL;| }|| return N;|

60 | }|| /* ---------------------------------------------------------------------- */|| void PrintTree1(Node *T)

Page 95: Standish Sol Manual Ece223

Table of Contents — continued

65 | {| if (T != NULL) {| if (IsOperator(T–>Symbol) ) {| putchar('(');| PrintTree1(T–>LLink);

70 | printf(" %c ",T–>Symbol);| PrintTree1(T–>RLink);| putchar(')');| } else {| putchar(T–>Symbol);

75 | }| }| }|| /* ---------------------------------------------------------------------- */

80 || void PrintTree(Node *T)| {| PrintTree1(T);| putchar('\n');

85 | }|| /* ---------------------------------------------------------------------- */|| int precedence(char c) /* define precedence of operators & atoms */

90 | {| switch (c) {| case '^':| return 3;| case '*':

95 | case '/':| return 2;| case '+':| case '–':| return 1;

100 | default :| return 4;| }| }|

105 | /* ---------------------------------------------------------------------- */|| void WriteP(char c)

/* write spaces around '+' and '–' */| { /* but don't write spaces around other operators */| if (precedence(c) == 1) {

110 | printf(" %c ",c);| } else {| putchar(c);| }| }

115 || /* ---------------------------------------------------------------------- */|| void PrintParens1(Node *T, char op) /* op is the operator above and */| { /* to the right of subtree T, where T is not atomic */

120 | if (T == NULL) {| ; /* print nothing */| } else if ( !IsOperator(T–>Symbol) ) {| putchar(T–>Symbol);| } else if (precedence(T–>Symbol) < precedence(op) ) {

125 | putchar('(');

Page 96: Standish Sol Manual Ece223

Table of Contents — continued

| PrintParens1(T–>LLink,T–>Symbol);| WriteP(T–>Symbol);| PrintParens2(T–>RLink,T–>Symbol);| putchar(')');

130 | } else {| PrintParens1(T–>LLink,T–>Symbol);| WriteP(T–>Symbol);| PrintParens2(T–>RLink,T–>Symbol);| }

135 | }|| /* ---------------------------------------------------------------------- */|| void PrintParens2(Node *T, char op)

140 | {| /* op is the operator above and to the left of subtree T, */| /* where T is not atomic */|| if (T == NULL) {

145 | ; /* print nothing */| } else if ( !IsOperator(T–>Symbol) ) {| putchar(T–>Symbol);| } else if ( (precedence(T–>Symbol) < precedence(op)) | || ( (precedence(T–>Symbol) == precedence(op)) &&

150 | ( !((T–>Symbol == op) && ((op == '+') | | (op == '*')) )) )) {| putchar('(');| PrintParens1(T–>LLink,T–>Symbol);| WriteP(T–>Symbol);| PrintParens2(T–>RLink,T–>Symbol);

155 | putchar(')');| } else {| PrintParens1(T–>LLink,T–>Symbol);| WriteP(T–>Symbol);| PrintParens2(T–>RLink,T–>Symbol);

160 | }| }|| /* ---------------------------------------------------------------------- */|

165 | void PrintParens3(Node *T)| {| if (T != NULL) {| if ( IsOperator(T–>Symbol) ) {| PrintParens1(T–>LLink,T–>Symbol);

170 | WriteP(T–>Symbol);| PrintParens2(T–>RLink,T–>Symbol);| } else {| putchar(T–>Symbol);| }

175 || }|| /* ---------------------------------------------------------------------- */|

180 | void PrintParens(Node *T)| {| PrintParens3(T);| putchar('\n');| }

185 || /* ---------------------------------------------------------------------- */|

Page 97: Standish Sol Manual Ece223

Table of Contents — continued

| int main(void)| {

190 | char *S = ".....................", /* empty string */| *S1 = "/–^b2**4ac*2a", /* first test case */| *S2 = "**+ag+bc*++cd+de++efg", /* second test case */| *S3 = "/^2+a*bc–e–fg", /* third test case */| *S4 = "/^2*a^bc––ehg", /* fourth test case */

195 | *S5 = "/^2*a^bc//xy/hg"; /* fifth test case */|| Node *T;| int i;|

200 | strcpy(S,S1); /* substitute Si for S1 for other test cases */| printf("translating %s:\n",S);| T = PrefixStringToTree(&S);|| PrintTree(T); /* print fully parenthesized tree to verify */

205 | /* tree construction. Ex 9.6.3 */| PrintParens(T); /* print parentheses only when required */| /* by operator precedence. Ex 9.6.4 */|| }

210 | /* main program */|

Answers to Review Questions 9.71. A binary search tree is a binary tree with keys stored at its nodes

such that for any node N with key K, the keys in the nodes of theleft subtree of the node rooted at N are less than K, and the keysin the nodes of the right subtree of the node rooted at N aregreater than K.

2. To search for a key K in a binary search tree T, if T is NULL thesearch fails. If K equals the key Kr stored in the root node of T, thesearch succeeds. Otherwise, if K < Kr, further binary search isconducted on the left subtree of the root node of T, and if K > Kr,further binary search is conducted on the right subtree of the rootnode of T.

3. The internal path length I, is the sum of all the path lengths fromthe root node to each internal node of tree T. The external pathlength E, is the sum of the path lengths from the root to eachexternal node of an extended binary tree, T. In a binary tree with ninternal nodes, we always have E = I + 2 n.

4. O-notations for various cases of successful search in a binarysearch tree n nodes:

(a) best case: O(log n)

(b) worst case: O(n)

(c) average case: O(log n)

Page 98: Standish Sol Manual Ece223

Table of Contents — continued

Solutions to Exercises 9.71. Leff Wright’s program is correct.

2. A recursive function to search for an airport code key A in abinary search tree T is given as follows:

| TreeNode *BinaryTreeSearch (AirportCode A, TreeNode *T)| {| int result;|

5 | if (T == NULL) {| return NULL;| } else if ( (result = strcmp(A,T–>Airport)) == 0) {| return (T); /* T points to the node containing A */| } else if (result < 0) {

10 | return BinaryTreeSearch(A,T–>LeftLink); /* search in */| /* the left subtree */| } else {| return BinaryTreeSearch(A,T–>RightLink); /* search in */| /* the right subtree */

15 | }| }

3. A TreeInsert function that inserts a new airport code A in tree T isgiven below. It uses the auxiliary function MakeNode.

| /* First we give the auxiliary function MakeNode(A) */|| TreeNode *MakeNode(AirportCode A)| {

5 | TreeNode *temp;|| temp = (TreeNode *) malloc(sizeof(TreeNode));| temp–>LeftLink = NULL;| temp–>RightLink = NULL;

10 | strcpy(temp–>Airport, A);| return temp;| }||

15 | /* The function TreeInsert(A,T) uses the auxiliary function MakeNode */|| TreeNode *TreeInsert(AirportCode A, TreeNode *T)| {| if (T == NULL) {

20 | return MakeNode(A);| } else if ( strcmp(A,T–>Airport) < 0 ) {| T–>LeftLink = TreeInsert(A,T–>LeftLink);| } else {| T–>RightLink = TreeInsert(A,T–>RightLink);

25 | }|| return T;| }

4. The tree printing function TreePr int (T) given below is helpful forverifying that the binary search tree of Fig. 9.31 can beconstructed by inserting the nodes of Table 9.32 into an initially

Page 99: Standish Sol Manual Ece223

Table of Contents — continued

empty binary search tree using the solution to Ex. 9.7.3 givenabove. A section of a main program which verifies that the solutionworks is also given below.

| /* -------------------------------------------------------------- */|| void AuxTreePrint(TreeNode *T)| {

5 | if (T != NULL) {| putchar('(');| AuxTreePrint(T->LeftLink);| printf(" %s ",T->Airport);| AuxTreePrint(T->RightLink);

10 | putchar(')');| }| }|| /* -------------------------------------------------------------- */

15 || void TreePrint(TreeNode *T)| {| if (T == NULL) {| printf("( )\n");

20 | } else {| AuxTreePrint(T);| putchar('\n');| }| }

25 || /* -------------------------------------------------------------- */|| int main(void)| {

30 | TreeNode *T;|| /* First insert the airport codes from Table 9.3.2 in the order given */|| T = NULL;

35 | T = TreeInsert("ORY",T);| T = TreeInsert("JFK",T);| T = TreeInsert("BRU",T);| T = TreeInsert("DUS",T);| T = TreeInsert("ZRH",T);

40 | T = TreeInsert("MEX",T);| T = TreeInsert("ORD",T);| T = TreeInsert("NRT",T);| T = TreeInsert("ARN",T);| T = TreeInsert("GLA",T);

45 | T = TreeInsert("GCM",T);|| /* Then print the tree T as a parenthesized expression */| TreePrint(T);| }

50 | /* end main */|

5. We can prove that E = I + 2n by induction, where n is the number ofinternal nodes. Starting with the base case, let n = 1. Then I = 0 (in

Page 100: Standish Sol Manual Ece223

Table of Contents — continued

) and E = 2 (in  ). So, E = 2 = 0 + 2*1 = I + 2n. Proceedingwith the induction step, suppose Eold = Iold + 2n for a tree with ninternal nodes. Choose a leaf at distance r from the root (where r ≥1). Substitute a new internal node ( ) for the leaf and add two newleaves ( ) as children of the new internal node, using thetransformation:

⇒ .

The number of internal nodes increased from n to n + 1. A leaf atdistance r was subtracted and two leaves at distance r + 1 wereadded. So,

Enew = Eold – r + 2(r + 1).

An internal node at distance r was added. So, Inew = Iold + r. Nowsubstituting in Eold = Iold + 2n,

⇒ Enew + r – 2(r + 1) = Inew – r + 2n

⇒ Enew + 2 r – 2r – 2 = Inew + 2n

⇒ Enew = Inew + 2n + 2 = Inew + 2(n + 1).

Therefore, Enew = Inew + 2(n + 1), which is what we wanted. Anytree of n ≥ 1 internal nodes can be built up from a tree of oneinternal node by repeatedly substituting new internal nodes for itsleaves.

6. When n takes the special form n = 2r – 1, where r is the number ofrows in the tree, we can simplify the formula for Ln, given in the“Math Reference Appendix,” as follows. First, if n = 2r – 1, then r =lg(n+1), and because r is an integer, we also have r = lg(n+1) = lg(n+1) . Equation A.31 in the Appendix gives a formula for Ln a sfollows:

Ln = ∑i   =   1

  n  l g   i = (n+1)q – 2q+1 + 2, where q = lg(n+1) .

Because q = r and because n = 2r – 1 implies that 2q+1 = 2(2r), wecan simplify the formula for Ln above to get Ln = (n+1)r – 2(2r) + 2.

Now, we can simplify the exact formula for the best case, Cn =(2Ln+n)/n, as follows:

(2Ln+n)/n = ( 2 [(n+1)r – 2(2r) + 2] + n) /n

= 2n [(n+1)r – 2(2r–1)] + 1

Page 101: Standish Sol Manual Ece223

Table of Contents — continued

= 2n [(n+1)r – 2 n] + 1

= 2 r + 2 r / n – 4 + 1

= ( 2r – 3 ) + 2 rn .

7. The functions below calculate the formulas for Ex. 9.7.7 and Ex.9.7.8:

| #include <math.h>|| /* -------------------------------------------------------------- */|

5 | double Hn(int n) /* computes n th Harmonic number Hn */| {| double Temp; int i;|| Temp = 0.0;

10 | for (i = 1; i <= n; ++i) Temp += 1.0/i;| return Temp;| }|| /* -------------------------------------------------------------- */

15 || double Lg(int n) /* Base 2 Logarithm */| {| return log(n)/log(2); /* log(n) is C's natural log function */| }

20 || /* -------------------------------------------------------------- */|| double Power(int n, int m) /* n^m */| {

25 | return exp(m*log(n));| }|| /* -------------------------------------------------------------- */|

30 | double L(int n) /* L = ∑ (1≤i≤n) Floor(lg(i)) */ | {| int q;|| q = (int) floor(Lg(n+1));

35 | return (n + 1)*q – Power(2,(q+1)) + 2;| }|| /* -------------------------------------------------------------- */|

40 | double ExactCn(int n)| {| return 4*(1 + 1.0/n)*Hn(n) – 7;| }|

45 | /* -------------------------------------------------------------- */|| double ApproxCn(int n)

Page 102: Standish Sol Manual Ece223

Table of Contents — continued

| {| return 2.77*Lg(n) – 4.7;

50 | }|| /* -------------------------------------------------------------- */|| int TestFormulas(void)

55 | {| int n;| double X, Y, Cn;|| /* Exercise 9.7.7 -- Compare Exact and Approximate Average */

60 | /* Case Formulas */|| printf("Exercise 9.7.7\n");|| n = 510;

65 | printf("Exact Average Cn for n = %d: Cn = %3.7f\n", n, ExactCn(n));| printf("Apprx Average Cn for n = %d: Cn = %3.7f\n", n, ApproxCn(n));|| n = 511;| printf("Exact Average Cn for n = %d: Cn = %3.7f\n", n, ExactCn(n));

70 | printf("Apprx Average Cn for n = %d: Cn = %3.7f\n", n, ApproxCn(n));|| n = 1023;| printf("Exact Average Cn for n = %d: Cn = %3.7f\n", n, ExactCn(n));| printf("Apprx Average Cn for n = %d: Cn = %3.7f\n", n, ApproxCn(n));

75 || putchar('\n');|| n = 1;| while (fabs(ExactCn(n) – ApproxCn(n))/ExactCn(n) > 0.02) {

80 | n++;| }|| printf("The n for which ApproxAverageCn and ");| printf("ExactAverageCn differ by ≤ 2%% = %d\n",n);

85 || putchar('\n');| printf("Exact Cn for n = 90: Cn = %3.7f\n", ExactCn(90));| printf("Apprx Cn for n = 90: Cn = %3.7f\n", ApproxCn(90));| printf("Exact Cn for n = 91: Cn = %3.7f\n", ExactCn(91));

90 | printf("Apprx Cn for n = 91: Cn = %3.7f\n", ApproxCn(91));||| printf("\n\n");|

95 | /* Exercise 9.7.8 -- Compare Exact and Approximate Best Case Formulas */|| printf("Exercise 9.7.8\n");|| n = 0;

100 || do {| n++;| X = ((2*L(n) + n)/n); /* exact best case for n */| Y = (2*Lg(n) – 3); /* approximate best case for n */

105 | } while ( (fabs(X – Y)/X) > 0.02 );|| printf("The least n for which ApproxBestCn and ");| printf("ExactBestCn differ by ≤ 2%% = %d\n",n);|

Page 103: Standish Sol Manual Ece223

Table of Contents — continued

110 | n = 115;| printf("Exact Best Cn for n = %d: Cn = %3.7f\n", n, ((2*L(n) + n)/n));| printf("Apprx Best Cn for n = %d: Cn = %3.7f\n", n, (2*Lg(n) – 3));|| n = 116;

115 | printf("Exact Best Cn for n = %d: Cn = %3.7f\n", n, ((2*L(n) + n)/n));| printf("Apprx Best Cn for n = %d: Cn = %3.7f\n", n, (2*Lg(n) – 3));|| }| /* end TestFormulas */|

When the above program is executed, it determines that the leastn for which the Approximate Average Cn and the Exact Average Cndiffer by ≤ 2% is n = 91. It also prints the following values of Cnrequested in Ex. 9.7.7:

for n = 511 for n = 512 for n = 1023

Exact Average Cn 20.312 20.319 23.062

Approx AverageCn

20.222 20.230 22.996

8. See the program above in the solution to Ex. 9.7.7. When thisprogram is executed, it determines that the least n for which theApproximate Best Cn and the Exact Best Cn differ by ≤ 2% is n =116. Hence, it is verified that these values also differ by less than2% for any n ≥ 197.

9. To delete a node with airport code A , call the functionDeleteNode(A,&T) in what follows, where T contains a pointer to theroot of the binary search tree:

| /*| * Ex. 9.7.9 -- Routines to delete a node in a binary search tree| */|

5 | /* -------------------------------------------------------------- */|| void DeleteSmallest(AirportCode *A, TreeNode **T)| {| TreeNode *N;

10 || if ( (*T)–>LeftLink == NULL ) {| strcpy(*A,(*T)–>Airport);| N = (*T)–>RightLink;| free(*T);

15 | *T = N;| } else {| DeleteSmallest(A,&(*T)–>LeftLink);| }| }

20 |

Page 104: Standish Sol Manual Ece223

Table of Contents — continued

| /* -------------------------------------------------------------- */|| void DeleteLargest(AirportCode *A, TreeNode **T)| {

25 | TreeNode *N;|| if ( (*T)–>RightLink == NULL ) {| strcpy(*A,(*T)–>Airport);| N = (*T)–>LeftLink;

30 | free(*T);| *T = N;| } else {| DeleteLargest(A,&(*T)–>RightLink);| }

35 | }|| /* -------------------------------------------------------------- */|| void DeleteRoot(TreeNode **T)

40 | {| AirportCode temp;|| if ( (*T)–>LeftLink == NULL ) {| if ( (*T)–>RightLink == NULL ) {

45 | free(*T);| *T = NULL;| } else {| DeleteSmallest(&temp, &(*T)–>RightLink);| strcpy( (*T)–>Airport, temp);

50 | }| } else {| DeleteLargest(&temp, &(*T)–>LeftLink);| strcpy( (*T)–>Airport, temp);| }

55 | }|| /* -------------------------------------------------------------- */|

| void DeleteNode(AirportCode A, TreeNode **T)60 | {

| int result;|| if ( (*T) == NULL ) {| ; /* the tree is empty and does not contain the airport code A */

65 | } else if ( ( result = strcmp(A, (*T)–>Airport)) == 0 ) {| DeleteRoot(T);| } else if (result < 0) {| DeleteNode(A, &(*T)–>LeftLink);| } else {

70 | DeleteNode(A, &(*T)–>RightLink);| }| }|| /* -------------------------------------------------------------- */

Answers to Review Questions 9.8

Page 105: Standish Sol Manual Ece223

Table of Contents — continued

1. The height of a binary tree is defined to be the length of thelongest path from the root to one of its leaves. As a special casethe height of the empty tree is defined to be –1.

2. Node N in tree T has the AVL property if the heights of N’s left andright subtrees differ by at most 1. An AVL tree is a binary searchtree in which each node has the AVL property.

3. See Fig. 9.44 on p. 379 of the text.

4. O(log n).

Solutions to Selected Exercises in Exercise 9.81. The basic idea is to add a field to each node in an AVL tree

containing 1 + the number of nodes in the left subtree of the node.Let us call such a field the Index of node N. If N contains a pointer toan AVL tree node, the Index of node N is given by N–>Index.

Then to get the length of a tree representing a list, we use:

| int Length(TreeNode *T)| {| if (T == NULL) {| return (0);

5 | } else {| return (T–>Index + Length(T–>RightLink));| }| }

To find a pointer to the node containing the ith item of T, we canuse:

| TreeNode *Find(int i, TreeNode *T)| {| if (T != NULL) {| if (i == T–>Index) {| return (T);| } else if (i < T–>Index) {| return (Find(i, T–>LeftLink);| } else {| return(Find(i – T–>Index, T–>RightLink);| }| } else {| return NULL; /* return NULL if the ith item doesn’t exist */| }| }

2. Yes, the InOrder traversal of a binary search tree reads the keysin the nodes in increasing order of the keys. It helps to use an AVLtree to keep the total insertion time bounded by O(n log n) wherethe sum of the O(log i) insertion times for the n keys Ki, (1 ≤ i ≤ n),starting with an empty tree, is O(n log n).

3. The solutions to Exs. 9.8.3 and 9.8.4 are given in the followingprogram which provides a complete set of AVL-tree algorithms

Page 106: Standish Sol Manual Ece223

Table of Contents — continued

for insertion, deletion, searching, and tree printing (with balancefactors shown). To insert an AirportCode A into an AVL tree T

(where T is a variable containing a TreeNode pointer to the root ofAVL tree T), you make the function call, InsertNode(A, &T, &Heavier),using the function InsertNode defined on lines 167:230. To delete anode containing the AirportCode A from the tree whose root isreferenced by the TreeNode pointer in the variable T, you make thefunction call, DeleteNode(A, &T, &Lighter), using the function DeleteNode

defined on lines 487:511.

| /*| * Complete AVL-tree programs including solutions to Exs. 9.8.3 and 9.8.4| */|

5 | #include <stdio.h>| #include <string.h>||| typedef char AirportCode[4];

10 || typedef enum {LeftHeavy, Balanced, RightHeavy} BalanceCode;|| typedef struct TreeNodeTag {| BalanceCode BalanceFactor;

15 | AirportCode Airport;| struct TreeNodeTag *LeftLink;| struct TreeNodeTag *RightLink;| } TreeNode;|

20 || /* some variables */|| TreeNode *T, *N;| Boolean Heavier, Lighter;

25 || /* -------------------------------------------------------------- */|| TreeNode *CreateNode(AirportCode A)| {

30 | TreeNode *N;|| N = (TreeNode *)malloc(sizeof(TreeNode)); /* create a new blank */| /* tree node which N references */| N–>LeftLink = NULL; /* set its left and right links to be NULL */

35 | N–>RightLink = NULL;| strcpy(N–>Airport,A); /* let its airport code be A */| N–>BalanceFactor = Balanced;| return N; /* and let N be the value of the function */| }

40 || /* -------------------------------------------------------------- */|| void RotateLeft(TreeNode **T) /* Assume *T points to a non-empty */| { /* tree having a non-empty right subtree */

45 | TreeNode *N; /* let N be a tree node pointer */|| N = (*T)–>RightLink;

Page 107: Standish Sol Manual Ece223

Table of Contents — continued

| (*T)–>RightLink = N–>LeftLink;| N–>LeftLink = (*T);

50 | (*T) = N;| }|| /* -------------------------------------------------------------- */|

55 | void RotateRight(TreeNode **T) /* Assume *T points to a non-empty */| { /* tree having a non-empty left subtree */| TreeNode *N; /* let N be a tree node pointer */|| N = (*T)–>LeftLink;

60 | (*T)–>LeftLink = N–>RightLink;| N–>RightLink = (*T);| (*T) = N;| }|

65 | /* -------------------------------------------------------------- */|| void RightRebalance(TreeNode **T, Boolean *Heavier)| {| TreeNode *N, *P;

70 || N = (*T)–>RightLink;|| switch (N–>BalanceFactor) {|

75 | case LeftHeavy: /* perform a double left rotation */|| P = N–>LeftLink;| switch (P–>BalanceFactor) {| case LeftHeavy:

80 | (*T)–>BalanceFactor = Balanced;| N–>BalanceFactor = RightHeavy;| break;| case Balanced:| (*T)–>BalanceFactor = Balanced;

85 | N–>BalanceFactor = Balanced;| break;| case RightHeavy:| (*T)–>BalanceFactor = LeftHeavy;| N–>BalanceFactor = Balanced;

90 | break;| }| P–>BalanceFactor = Balanced;| RotateRight(&N);| (*T)–>RightLink = N;

95 | RotateLeft(T);| *Heavier = false;| break;|| case Balanced:

100 | /* do nothing -- this case cannot occur */| break;|| case RightHeavy: /* perform a single left rotation */|

105 | (*T)–>BalanceFactor = Balanced;| N–>BalanceFactor = Balanced;| RotateLeft(T);| *Heavier = false;| break;

Page 108: Standish Sol Manual Ece223

Table of Contents — continued

110 || }|| }|

115 | /* -------------------------------------------------------------- */|| void LeftRebalance(TreeNode **T, Boolean *Heavier)| {| TreeNode *N, *P;

120 || N = (*T)–>LeftLink;|| switch (N–>BalanceFactor) {|

125 | case LeftHeavy: /* perform a single right rotation */| (*T)–>BalanceFactor = Balanced;| N–>BalanceFactor = Balanced;| RotateRight(T);| *Heavier = false;

130 | break;|| case Balanced:|| /* do nothing -- this case cannot occur */

135 | break;|| case RightHeavy: /* perform a double right rotation */|| P = N–>RightLink;

140 | switch (P–>BalanceFactor) {| case LeftHeavy:| (*T)–>BalanceFactor = RightHeavy;| N–>BalanceFactor = Balanced;| break;

145 | case Balanced:| (*T)–>BalanceFactor = Balanced;| N–>BalanceFactor = Balanced;| break;| case RightHeavy:

150 | (*T)–>BalanceFactor = Balanced;| N–>BalanceFactor = LeftHeavy;| break;| }| P–>BalanceFactor = Balanced;

155 | RotateLeft(&N);| (*T)–>LeftLink = N;| RotateRight(T);| *Heavier = false;| break;

160 || }|| }|

165 | /* -------------------------------------------------------------- */|| void InsertNode(AirportCode A, TreeNode **T, Boolean *Heavier)| {| TreeNode *P, *N;

170 | Boolean HeavierSubtree;|

Page 109: Standish Sol Manual Ece223

Table of Contents — continued

| /* If T was the empty tree, then change it to be a pointer to */| /* a new node with airport code A, and return */|

175 | if ((*T) == NULL) {|| *T = CreateNode(A);| *Heavier = true;|

180 | } else {|| if ( strcmp(A,(*T)–>Airport) < 0 ) {|| InsertNode(A,&(*T)–>LeftLink,&HeavierSubtree);

185 || if (HeavierSubtree) {| switch ((*T)–>BalanceFactor) {| case LeftHeavy:| LeftRebalance(T,Heavier);

190 | break;| case Balanced:| (*T)–>BalanceFactor = LeftHeavy;| *Heavier = true;| break;

195 | case RightHeavy:| (*T)–>BalanceFactor = Balanced;| *Heavier = false;| break;| }

200 | } else {| *Heavier = false;| }|| } else { /* we must have had A > (*T)–>Airport */

205 || InsertNode(A, &(*T)–>RightLink, &HeavierSubtree);|| if (HeavierSubtree) {| switch ( (*T)–>BalanceFactor) {

210 | case LeftHeavy:| (*T)–>BalanceFactor = Balanced;| *Heavier = false;| break;| case Balanced:

215 | (*T)–>BalanceFactor = RightHeavy;| *Heavier = true;| break;| case RightHeavy:| RightRebalance(T,Heavier);

220 | break;| }| } else {| *Heavier = false;| }

225 || } /* end if */|| } /* end if */|

230 | } /* end InsertNode */|| /* -------------------------------------------------------------- */|

Page 110: Standish Sol Manual Ece223

Table of Contents — continued

| void RebalanceToLeft(TreeNode **T, Boolean *Lighter)235 | {

| /* Rebalance after T's left subtree became lighter */|| TreeNode *N, *P;|

240 | switch ( (*T)–>BalanceFactor ) {|| case LeftHeavy:| (*T)–>BalanceFactor = Balanced;| *Lighter = true;

245 | break;|| case Balanced:| (*T)–>BalanceFactor = RightHeavy;| *Lighter = false;

250 | break;|| case RightHeavy:| N = (*T)–>RightLink;|

255 | switch (N–>BalanceFactor) {|| case LeftHeavy:| P = N–>LeftLink;| switch (P–>BalanceFactor) {

260 || case LeftHeavy:| (*T)–>BalanceFactor = Balanced;| N–>BalanceFactor = RightHeavy;| break;

265 || case Balanced:| (*T)–>BalanceFactor = Balanced;| N–>BalanceFactor = Balanced;| break;

270 || case RightHeavy:| (*T)–>BalanceFactor = LeftHeavy;| N–>BalanceFactor = Balanced;| break;

275 | }|| RotateRight(&N); /* double left rotation rooted at T */| (*T)–>RightLink = N;| RotateLeft(T);

280 | *Lighter = true;| break;|| case Balanced:| (*T)–>BalanceFactor = RightHeavy;

285 | N–>BalanceFactor = LeftHeavy;| RotateLeft(T);| *Lighter = false;| break;|

290 | case RightHeavy:| (*T)–>BalanceFactor = Balanced;| N–>BalanceFactor = Balanced;| RotateLeft(T);| *Lighter = true;

300 | break;

Page 111: Standish Sol Manual Ece223

Table of Contents — continued

|| } /* end switch */| break;|

305 | } /* end switch */|| } /* RebalanceToLeft */|| /* -------------------------------------------------------------- */

310 || void RebalanceToRight(TreeNode **T, Boolean *Lighter)| {| /* Rebalance after T's right subtree became lighter */|

315 | TreeNode *N, *P;|| switch ((*T)–>BalanceFactor) {|| case LeftHeavy:

320 || N = (*T)–>LeftLink;| switch (N–>BalanceFactor) {|| case LeftHeavy:

325 | (*T)–>BalanceFactor = Balanced;| N–>BalanceFactor = Balanced;| RotateRight(T);| *Lighter = true;| break;

330 || case Balanced:| (*T)–>BalanceFactor = LeftHeavy;| N–>BalanceFactor = RightHeavy;| RotateRight(T);

335 | *Lighter = false;| break;|| case RightHeavy:|

340 | P = N–>RightLink;| switch (P–>BalanceFactor) {|| case LeftHeavy:| (*T)–>BalanceFactor = RightHeavy;

345 | N–>BalanceFactor = Balanced;| break;|| case Balanced:| (*T)–>BalanceFactor = Balanced;

350 | N–>BalanceFactor = Balanced;| break;|| case RightHeavy:| (*T)–>BalanceFactor = Balanced;

355 | N–>BalanceFactor = LeftHeavy;| break;|| }| RotateLeft(&N); /* double right rotation rooted at T */

360 | (*T)–>LeftLink = N;| RotateRight(T);| *Lighter = true;

Page 112: Standish Sol Manual Ece223

Table of Contents — continued

| break;|

365 | } /* end switch */| break;|| case Balanced:| (*T)–>BalanceFactor = LeftHeavy;

370 | *Lighter = false;| break;|| case RightHeavy:| (*T)–>BalanceFactor = Balanced;

375 | *Lighter = true;| break;|| } /* end switch */|

380 | } /* RebalanceToRight */|| /* -------------------------------------------------------------- */|| void DeleteSmallest(AirportCode *A,TreeNode **T, Boolean *Lighter)

385 | {| TreeNode *temp;| Boolean SubtreeLighter;|| if ( (*T)–>LeftLink == NULL) {

390 | if ( (*T)–>RightLink == NULL) {| strcpy(*A,(*T)–>Airport);| free(*T);| (*T) = NULL;| *Lighter = true;

395 | } else {| strcpy(*A,(*T)–>Airport);| temp = (*T);| (*T) = (*T)–>RightLink;| free(temp);

400 | *Lighter = true;| }| } else { /* neither (*T)–>LeftLink nor (*T)–>RightLink is NULL */| DeleteSmallest(A,&(*T)–>LeftLink,&SubtreeLighter);| if (SubtreeLighter) RebalanceToLeft(T,Lighter);

405 | }| }|| /* -------------------------------------------------------------- */|

410 | void DeleteLargest(AirportCode *A, TreeNode **T, Boolean *Lighter)| {| TreeNode *temp;| Boolean SubtreeLighter;|

415 | if ( (*T)–>RightLink == NULL ) {| if ( (*T)–>LeftLink == NULL ) {| strcpy(*A,(*T)–>Airport);| free(*T);| (*T) = NULL;

420 | *Lighter = true;| } else {| strcpy(*A,(*T)–>Airport);| temp = (*T);| (*T) = (*T)–>LeftLink;

Page 113: Standish Sol Manual Ece223

Table of Contents — continued

425 | free(temp);| *Lighter = true;| }| } else { /* neither (*T)–>LeftLink nor (*T)–>RightLink is NULL */| DeleteLargest(A,&(*T)–>RightLink,&SubtreeLighter);

430 | if (SubtreeLighter) RebalanceToRight(T,Lighter);| }| }|| /* -------------------------------------------------------------- */

435 || void DeleteRoot(TreeNode **T, Boolean *Lighter)| {| AirportCode A;| Boolean SubtreeLighter;

440 || if ( ((*T)–>RightLink == NULL) && ((*T)–>LeftLink == NULL) ) {|| free(*T);| (*T) = NULL;

445 | *Lighter = true;|| } else {|| switch ((*T)–>BalanceFactor) {

450 | case LeftHeavy:| DeleteLargest(&A,&(*T)–>LeftLink,&SubtreeLighter);| strcpy((*T)–>Airport,A);| if (SubtreeLighter) {| (*T)–>BalanceFactor = Balanced;

455 | *Lighter = true;| } else {| *Lighter = false;| }| break;

460 || case Balanced:| DeleteSmallest(&A,&(*T)–>RightLink,&SubtreeLighter);| strcpy((*T)–>Airport,A);| if (SubtreeLighter) (*T)–>BalanceFactor = LeftHeavy;

465 | *Lighter = false;| break;|| case RightHeavy:| DeleteSmallest(&A,&(*T)–>RightLink,&SubtreeLighter);

470 | strcpy((*T)–>Airport,A);| if (SubtreeLighter) {| (*T)–>BalanceFactor = Balanced;| *Lighter = true;| } else {

475 | *Lighter = false;| }| break;|| } /* end switch */

480 || } /* end if */|| } /* DeleteRoot */|

485 | /* -------------------------------------------------------------- */|

Page 114: Standish Sol Manual Ece223

Table of Contents — continued

| void DeleteNode(AirportCode A,TreeNode **T, Boolean *Lighter)| {| Boolean SubtreeLighter;

490 | int result;|| if ((*T) == NULL) { /* AirportCode was not in tree T */| *Lighter = false;| } else if ( (result = strcmp(A,(*T)–>Airport)) == 0){

495 | DeleteRoot(T,Lighter);| } else if (result < 0 ) {| DeleteNode(A,&(*T)–>LeftLink,&SubtreeLighter);| if (SubtreeLighter) {| RebalanceToLeft(T,Lighter);

500 | } else { /* left subtree did not get lighter after deletion */| *Lighter = false;| }| } else { /* A > T–>Airport */| DeleteNode(A,&(*T)–>RightLink,&SubtreeLighter);

505 | if (SubtreeLighter) {| RebalanceToRight(T,Lighter);| } else { /* right subtree did not get lighter after deletion */| *Lighter = false;| }

510 | }| } /* DeleteNode */|| /* -------------------------------------------------------------- */|

515 || TreeNode *TreeSearch(AirportCode A, TreeNode *T)| {| TreeNode *N;| int result;

520 || /* Start with N pointing to the root of the tree T */|| N = T;|

525 | /* Advance the pointer N down the tree until finding the node with A */|| while (N != NULL) {| if ( (result = strcmp(A,N–>Airport)) == 0) {| return N;

530 | } else if (result < 0) {| N = N–>LeftLink;| } else {| N = N–>RightLink;| }

535 | }||| /* If no node in tree T contains A and N becomes NULL, */| /* we return NULL */

540 || return N;|| } /* TreeSearch */|

545 || /* -------------------------------------------------------------- */|| void PrintBalanceFactor(TreeNode *T)

Page 115: Standish Sol Manual Ece223

Table of Contents — continued

| {550 | if (T != NULL) {

|| switch (T–>BalanceFactor) {|| case LeftHeavy:

555 | putchar('–');break;|| case RightHeavy:| putchar('+');break;|

560 | case Balanced:| putchar('=');break;| }| }| }

565 || /* -------------------------------------------------------------- */|| void PrintTree(TreeNode *T)| {

570 || if (T != NULL) {|| if (T–>LeftLink != NULL) {| putchar('(');

575 | PrintTree(T–>LeftLink);| putchar(')');| }|| printf(" %s",T–>Airport);

580 | PrintBalanceFactor(T);| putchar(' ');|| if (T–>RightLink != NULL) {| putchar('(');

585 | PrintTree(T–>RightLink);| putchar(')');| }| } else {| printf("( )"); /* the null tree prints as ( ) */

590 | }| }|| /* -------------------------------------------------------------- */|

595 | void Delete(AirportCode A)| {| printf("\nAbout to delete %s: ",A);| DeleteNode(A,&T,&Lighter);| PrintTree(T);

600 | putchar('\n');| }|| /* -------------------------------------------------------------- */|

605 | int main(void)| {|| /* Construct the AVL tree of Fig. 9.46 on p. 382 */| T = NULL;

610 | InsertNode("ORY",&T,&Heavier);

Page 116: Standish Sol Manual Ece223

Table of Contents — continued

| InsertNode("JFK",&T,&Heavier);| InsertNode("BRU",&T,&Heavier);|| /* Print the Tree */

615 | PrintTree(T);| putchar('\n');|| InsertNode("DUS",&T,&Heavier);| InsertNode("ZRH",&T,&Heavier);

620 | InsertNode("MEX",&T,&Heavier);| InsertNode("ORD",&T,&Heavier);| InsertNode("NRT",&T,&Heavier);|| /* Print the Tree */

625 | PrintTree(T);| putchar('\n');|| InsertNode("ARN",&T,&Heavier);| InsertNode("GLA",&T,&Heavier);

630 | InsertNode("GCM",&T,&Heavier);|| /* Print the Tree */| PrintTree(T);| putchar('\n');

635 ||| /* Create a Binary Search Tree T pointing to 15 airports */| /*| T = NULL;

640 | InsertNode("MEX",&T,&Heavier);| InsertNode("GCM",&T,&Heavier);| InsertNode("ORY",&T,&Heavier);| InsertNode("BRU",&T,&Heavier);| InsertNode("HKG",&T,&Heavier);

645 | InsertNode("NRT",&T,&Heavier);| InsertNode("YYZ",&T,&Heavier);| */| /*| InsertNode("ARN",&T,&Heavier);

650 | InsertNode("DUS",&T,&Heavier);| InsertNode("GLA",&T,&Heavier);| InsertNode("JFK",&T,&Heavier);| InsertNode("MIA",&T,&Heavier);| InsertNode("ORD",&T,&Heavier);

655 | InsertNode("SAN",&T,&Heavier);| InsertNode("ZRH",&T,&Heavier);| */|| /* Print the Tree */

660 | /*| PrintTree(T);| putchar('\n');|| Delete("NRT"); /* does DeleteNode("NRT",&T,&Lighter); and prints tree */

665 | Delete("ORY"); /* does DeleteNode("ORY",&T,&Lighter); and prints tree */| Delete("YYZ"); /* does DeleteNode("YYZ",&T,&Lighter); and prints tree */| Delete("GCM");/* does DeleteNode("GCM",&T,&Lighter); and prints tree */| Delete("HKG"); /* does DeleteNode("HKG",&T,&Lighter); and prints tree */| Delete("MEX"); /* does DeleteNode("MEX",&T,&Lighter); and prints tree */

670 | Delete("BRU"); /* does DeleteNode("BRU",&T,&Lighter); and prints tree */| putchar('\n');| */

Page 117: Standish Sol Manual Ece223

Table of Contents — continued

|| /* Create a Binary Search Tree T pointing to 15 airports */

675 | /*| T = NULL;| InsertNode("MEX",&T,&Heavier);| InsertNode("GCM",&T,&Heavier);| InsertNode("ORY",&T,&Heavier);

680 | InsertNode("BRU",&T,&Heavier);| InsertNode("HKG",&T,&Heavier);| InsertNode("NRT",&T,&Heavier);| InsertNode("YYZ",&T,&Heavier);| InsertNode("ARN",&T,&Heavier);

685 | InsertNode("DUS",&T,&Heavier);| InsertNode("GLA",&T,&Heavier);| InsertNode("JFK",&T,&Heavier);| InsertNode("MIA",&T,&Heavier);| InsertNode("ORD",&T,&Heavier);

690 | InsertNode("SAN",&T,&Heavier);| InsertNode("ZRH",&T,&Heavier);| */|| /* Print the Tree */

695 | /*| PrintTree(T);| putchar('\n');| */|

700 | /* Do Tree Searches */|| /*| N = TreeSearch("MEX",T);| N = TreeSearch("GCM",T);

705 | N = TreeSearch("ORY",T);| N = TreeSearch("BRU",T);| N = TreeSearch("HKG",T);| N = TreeSearch("NRT",T);| N = TreeSearch("YYZ",T);

710 | N = TreeSearch("ARN",T);| N = TreeSearch("DUS",T);| N = TreeSearch("GLA",T);| N = TreeSearch("JFK",T);| N = TreeSearch("MIA",T);

715 | N = TreeSearch("ORD",T);| N = TreeSearch("SAN",T);| N = TreeSearch("ZRH",T);| */|

720 | }| /* end main */

4. The answer to Ex. 9.8.4 is given in the program above in thesolution to Ex. 9.8.3.

5. Yes, AVL trees can be used to organize the free blocks in adynamic memory allocation algorithm so that block allocationtakes O(log n) time. The details are in the hint in Ex. 9.8.5 on p. 388.

Answer to Review Question 9.9

Page 118: Standish Sol Manual Ece223

Table of Contents — continued

1. A 2–3 tree is a tree such that each non-empty node contains either2 or 3 keys, such that all empty trees lie on the same bottomlevel, and such that the InOrder traversal of the tree generatesthe keys in increasing order.

2. The disadvantage of attempting to maintain perfect balance isthat, when we attempt to insert or delete nodes, costlyrebalancing may be required in which the relative position in thetree of every node has to be rearranged. A 2–3 tree allows allsubtrees to be perfectly balanced with respect to heights, butcompromises on perfect balance by allowing nodes to have eithertwo or three subtrees as descendants. This compromise in perfectbalance allows for efficient insertion and deletion.

3. The branching factor of a node N in a tree T is the number ofdescendants of node N in tree T. If all internal nodes have the samenumber of descendants b, we say that b is the branching factor oftree T.

4. The larger the branching factor the smaller the number of levelsneeded to store a given number of keys n in a completely balancedtree. It is advantageous to use trees with high branching factorswhen attempting to search trees containing large numbers ofnodes stored in external memory devices for which the cost ofaccessing nodes is relatively high, because the cost of search isproportional to the number of levels that need to be searched.

Solutions to Selected Exercises in Exercise 9.9

1. The tree is drawn below.

D

E F IA B K M

J N

H

L

O Q S

R

P

Page 119: Standish Sol Manual Ece223

Table of Contents — continued

2. The deepest B-tree of order m containing n nodes can beconstructed by taking the minimum number of children allowed foreach node according to the definition of B-trees. Thus, the rootwill have two children, and each internal node on levels below theroot will have m/2 children. So the number of nodes on levels 1, 2,3, ..., follows the geometric progression: m/2 , m/2 2, m/2 3, ... . Ifall leaves appear on the level having level number k, the number ofleaves is a term in this progression of the form 2 m/2 k–1. But thenumber of leaves is just one more than the number of keys (as canbe seen by enumerating the leaves and keys of a B-tree ingeneralized InOrder, which yields a sequence in which leaves andkeys alternate, and in which leaves are at both ends of thesequence). Hence, n + 1 = #Keys + 1 = #leaves. Therefore, for thedeepest tree, k is the largest integer for which,

n + 1 ≥ 2 m/2 k–1.

Solving for k yields,

k ≤ 1 + log m /2

n+1

2 .

So,

k = 1 + log m /2

n+1

2 .

Answers to Review Questions 9.101. The word trie is pronounced so as to rhyme with pie or try.

2. A tr ie is a tree organized to use the ith character c of a key toselect the c th subtree on level i of a node at level i – 1. Forexample, suppose keys are spelled from letters of the alphabet.For a given key K, the first letter K[0 ] of K is used to select asubtree of the root of the trie (where each of the subtrees of theroot corresponds to a different first letter of the key K). Thesecond letter of K, which is K[1 ] (if there is one) selects thegrandchild subtree of the root, and so on. When the charactersassociated with the edges of the path from the root to a node Nspell a unique prefix p of a key K (i.e., if K is the only key having aprefix p among all keys stored in the trie) then K is stored in nodeN.

3. The shape of a trie is insensitive to the order of insertion of keys.Tries have remarkably good logarithmic search times. However,the efficiency of tries can be reduced if there is clustering in theprefixes of the keys (i.e., if many groups of keys share longprefixes).

Page 120: Standish Sol Manual Ece223

Table of Contents — continued

Solutions to Selected Exercises in Exercise 9.101. Tries for searching 3-letter airport codes could be represented

by linked lists to save space, or by a 3-level trie with nodes eachconsisting of pointer arrays of length 26 indexed by the letters ofthe alphabet (to save look-up time). In the case of a dictionary of65,000 words with a largest word length of 33, the nodes could berepresented by pointer arrays indexed by letters if the nodeswere full (say ≥ x% occupied by non-null subtree pointers), or (tosave space) by a sequence of pairs (l, p) where l is a letter and pis a subtree pointer, arranged in ascending order of l, so thatbinary searching could be used to locate a particular pair (l, p ) ,given l as a search key.

Answers to Review Questions 9.111. A Huffman Code is a variable-length binary code (formed from

strings over the alphabet {1, 0}) in which shorter bit strings areused to represent frequently used letters and longer bit stringsare used to represent less frequently used letters in such a way asto minimize encoded message lengths for messages which useletters with the same relative frequencies as those used toconstruct the Huffman Code.

2. A Huffman Coding Tree is a binary tree having 0’s and 1’s labellingits branches and having letters on its leaves. The successive 0’sand 1’s labelling the branches along a path from the root to a leafcontaining the letter L form the Huffman code for the letter L.Given a bit string equal to the Huffman code for letter L, itssuccessive 0’s and 1’s can be used to select the successivebranches of the Huffman Coding Tree starting with the root,identifying a path leading to the leaf containing L.

3. A Huffman Code minimizes the length of an encoded messagehaving the same relative letter frequencies as those used toconstruct the Huffman code, thus achieving a shorter encodedmessage length than a fixed length code.

Solutions to Selected Exercises in Exercise 9.111. STINT is encoded as 101111101010011 and SINE is encoded as

101110101000.

2. 100101010001011 decodes as NINES and 101010011010011decodes as INTENT.

3. The solution to Ex. 9.11.3 is given in the following program:

| /*

Page 121: Standish Sol Manual Ece223

Table of Contents — continued

| * HuffmanCodes -- solution to Ex. 9.11.3| */|

5 | #include <stdio.h>| #include <stdlib.h>| #include <string.h>||

10 | #define MaxCount 26|| typedef struct ItemTag {| char Letter;| int Frequency;

15 | char *Code;| struct ItemTag *LLink;| struct ItemTag *RLink;| } ItemStruct;|

20 || ItemStruct *ItemArray[MaxCount];|| /* ---------------------------------------------------------------------- */|

25 | void InitItemArray(void)| {| int i;| char *Letters = "ETAONISRHLDCUPFMWYBGVKQXJZ";| ItemStruct *N;

30 || for (i = 0; i< MaxCount; ++i) {| N = (ItemStruct *) malloc(sizeof(ItemStruct));| N–>Letter = Letters[i];| N–>LLink = NULL;

35 | N–>RLink = NULL;| ItemArray[i] = N;| }|| /* input letter frequencies from Table 9.52, followed by */

40 | /* Letter – Huffman Code assigned by the algorithm given below */|| ItemArray[ 0]–>Frequency = 1231; /* E - 100 */| ItemArray[ 1]–>Frequency = 959; /* T - 110 */| ItemArray[ 2]–>Frequency = 805; /* A - 0000 */

45 | ItemArray[ 3]–>Frequency = 794; /* O - 0001 */| ItemArray[ 4]–>Frequency = 719; /* N - 0011 */| ItemArray[ 5]–>Frequency = 718; /* I - 0100 */| ItemArray[ 6]–>Frequency = 659; /* S - 0110 */| ItemArray[ 7]–>Frequency = 603; /* R - 1010 */

50 | ItemArray[ 8]–>Frequency = 514; /* H - 1011 */| ItemArray[ 9]–>Frequency = 403; /* L - 00100 */| ItemArray[10]–>Frequency = 365; /* D - 01010 */| ItemArray[11]–>Frequency = 320; /* C - 01110 */| ItemArray[12]–>Frequency = 310; /* U - 01111 */

55 | ItemArray[13]–>Frequency = 229; /* P - 11100 */| ItemArray[14]–>Frequency = 228; /* F - 11101 */| ItemArray[15]–>Frequency = 225; /* M - 11110 */| ItemArray[16]–>Frequency = 203; /* W - 001010 */| ItemArray[17]–>Frequency = 188; /* Y - 001011 */

60 | ItemArray[18]–>Frequency = 162; /* B - 010110 */| ItemArray[19]–>Frequency = 161; /* G - 010111 */| ItemArray[20]–>Frequency = 93; /* V - 111111 */| ItemArray[21]–>Frequency = 52; /* K - 1111101 */

Page 122: Standish Sol Manual Ece223

Table of Contents — continued

| ItemArray[22]–>Frequency = 20; /* Q - 11111001 */65 | ItemArray[23]–>Frequency = 20; /* X - 111110000 */

| ItemArray[24]–>Frequency = 10; /* J - 1111100010 */| ItemArray[25]–>Frequency = 9; /* Z - 1111100011 */||

70 | /* if the ItemArray is not sorted in descending order of */| /* frequencies, sort it here */|| } /* InitItemArray */|

75 | /* ---------------------------------------------------------------------- */|| void ConstructHuffmanTree(void)| {| int i,j;

80 | ItemStruct *N;| Boolean NotFinished;|| for (i = MaxCount–1; i>0; ––i) {| N = (ItemStruct *) malloc(sizeof(ItemStruct));

85 | N–>Frequency| = ItemArray[i]–>Frequency+ItemArray[i–1]–>Frequency;| N–>RLink = ItemArray[i];| N–>LLink = ItemArray[i–1];|

90 | /* insert the new node in proper order of its frequency in */| /* the Item Array */|| j = i–2;| NotFinished = (j>=0);

95 || while (NotFinished) {| if (ItemArray[j]–>Frequency < N–>Frequency) {| ItemArray[j+1] = ItemArray[j]; /* shift one space */| /* right to make a hole */

100 | j––;| NotFinished = (j >= 0);| } else {| NotFinished = false;| }

105 | }|| ItemArray[j+1] = N; /* final placement of the new node N */|| } /* end for */

110 || } /* ConstructHuffmanTree */|| /* ---------------------------------------------------------------------- */|

115 | char *Concat(char *S, char c)| {| char *T,*temp;|| T = (char *)malloc(2+strlen(S)); /* allocate space for string */

120 | temp = T;|| while ((*T++ = *S++) != '\0') { /* transfer chars of S into T */| ;| }

125 | *(––T) = c; /* append new character c to end of string T */

Page 123: Standish Sol Manual Ece223

Table of Contents — continued

| *(++T) = '\0'; /* terminate T properly with a null character '\0' */|| return temp;| }

130 || /* ---------------------------------------------------------------------- */|| void AssignHuffmanCodes(ItemStruct *N, char *CodeToAssign)| {

135 | ItemStruct *L, *R; /* Assume N is non-null */|| N–>Code = CodeToAssign;| L = N–>LLink;| R = N–>RLink;

140 | if (L != NULL) AssignHuffmanCodes(L,Concat(CodeToAssign,'0'));| if (R != NULL) AssignHuffmanCodes(R,Concat(CodeToAssign,'1'));|| } /* AssignHuffmanCodes */|

145 | /* ---------------------------------------------------------------------- */|| void PrintHuffmanCodes(ItemStruct *N)| {| if (N != NULL) {

150 | if ((N–>RLink == NULL) && (N–>LLink == NULL)) {| printf("%c – %s\n", N–>Letter, N–>Code);| }| PrintHuffmanCodes(N–>LLink);| PrintHuffmanCodes(N–>RLink);

155 | }|| } /* PrintHuffmanCodes */|| /* ---------------------------------------------------------------------- */

160 || int main(void)| {| char *Test = "", *V;|

165 || InitItemArray();| ConstructHuffmanTree();| AssignHuffmanCodes(ItemArray[0],"");| PrintHuffmanCodes(ItemArray[0]);

170 || }| /* main program */|

Page 124: Standish Sol Manual Ece223

Table of Contents — continued

Answers to Review Questions 10.21. A graph G = (V,E) is a set of distinct vertices V together with a set

of edges E consisting of pairs of distinct vertices of V. In anundirected graph, the edges {vi, v j} are unordered pairs (or sets)of distinct vertices vi and vj. In a directed graph each edge (vi, vj)is an ordered pair of vertices in which the direction of travel alongthe edge is from vi to vj.

2. In an undirected graph there is no direction of travel along theedges, whereas in a directed graph, each edge e = (v i, v j)prescribes a direction of travel starting at the origin of the edgevi and ending at the terminus of the edge vj.

3. A path in a graph G = (V, E) is a sequence of vertices v1, v2, ... , vnsuch that there is an edge (vi, vi+1) in E for each i in (1 ≤ i < n). Inother words, a path is a sequence of vertices each of which isadjacent to the next vertex in the sequence. A cycle is a path p =v1, v2, ... , vn such that v1 = vn, meaning that a cycle is a path thatforms a loop by starting and ending at the same vertex.

4. Two vertices vi and vj in a graph G are connected if there is a pathin G starting at vi and ending at vj.

5. A connected component of a graph G = (V, E) is a subset S of thevertices V such that any two distinct vertices v i and v j in S areconnected.

6. An adjacency set Vx for a vertex x in a graph G = (V, E) is the setof all vertices y such that (x, y) is an edge in E. In symbols, Vx = { y| y ∈ V and (x, y) ∈ E}

7. If x is a vertex in a directed graph G = (V, E) the in-degree of x isthe number of distinct vertices y ∈ V such that (y, x) is an edge inE, and the out-degree of x is the number of distinct vertices y ∈ Vsuch that (x, y) is an edge in E. Put differently, the in-degree of xis the number of predecessors of x in G, and the out-degree of x isthe number of successors of x in G.

Solutions to Selected Exercises in Exercises 10.2

1. Vertex 2 is the only vertex of degree 3 in the graph of Figure 10.2.

2. V1 = { 2,3} V5 = {4, 6}

V2 = {1, 3, 4} V6 = {4, 5, 7, 8}

V3 = {1, 2} V7 = {4, 6}

Page 125: Standish Sol Manual Ece223

Table of Contents — continued

V4 = {2, 5, 6, 7} V8 = {6}

Answers to Review Questions 10.31. An adjacency matrix for a graph G = (V, E) having n vertices v1, v2,

... , vn is an n × n array T such that T[i, j] = 1 if and only if (vi, vj) isan edge in E and such that T[i, j] = 0 if and only if (vi, vj) is not anedge in E. The adjacency matrix for an undirected graph G is alwaysa symmetric matrix with a zero diagonal such that T[i, j] = T[j, i] forall 1 ≤ i, j ≤ n and T[i, i] = 0 for all 1 ≤ i ≤ n . Here T is symmetricbecause if v i is adjacent to v j in an undirected graph, then v j isalso adjacent to v i. In a directed graph, however, the adjacencymatrix need not be symmetric because it is possible to have adirected edge traveling from v i to v j without there being adirected edge in the reverse direction from vj to v i. Here, T[i, j] =1 but T[j, i] = 0.

2. If we look at row Ri, corresponding to the vertex v i in theadjacency matrix T for a graph G = (V, E), row Ri has a one in eachcolumn j for which an edge e = (vi, vj) exists in E, and Ri has a zeroin each column j for which no such edge (v i, v j) exists. We canrepresent the information in row Ri as a bit-vector in C by using asequence of n bits packed together, Ri = b1 b2 ... bn, such that bj =T [ i, j]. In C we can support this representation by a b i t -vectorrepresentation of sets as follows. Let SetSize be the number ofelements in the set, and assume that the set elements arerepresented by the non-negative integers in the range 0:SetSize – 1.Let ByteSize represent the number of bits in a byte. We representthe set by a contiguous array of bytes large enough to hold one bitfor each separate element in the set. If SetSize is an exact multipleof ByteSize, the number of bytes we need is SetSize / ByteSize, but ifSetSize is not an exact multiple of ByteSize (i.e., if SetSize % ByteSize != 0)then we need to use a number of bytes equal to 1 + SetSize / ByteSize.Thus, the number of bytes to use is given by ByteLength = (SetSize %

ByteSize != 0) ? 1 : 0) + SetSize / ByteSize. We can then give a type definitionfor a Set, as follows:

typedef char Set[ByteLength];

The following function Member( i ,S) determines (by extraction andshifting) the value of the ith bit in the bit-vector representationof the set S.

| int Member (int i, Set S) /* to return the value (0 or 1) of the ith bit */| { /* in the bit-vector */| return ( (S[i/ByteSize] >> (i% ByteSize) ) & 1 ); /* representing set S */| }

Page 126: Standish Sol Manual Ece223

Table of Contents — continued

To represent a graph G = (V, E), one constructs an adjacencymatrix T for G using rows Ri represented by bit-vectorscorresponding to the rows.

3. The adjacency sets Vx can be used in a graph representation of agraph V = (G, E) to represent the set of edges (x, y) in E having xas one end-point and having another distinct vertex y as the otherend-point.

4. In a directed graph the in-degree of a vertex x is the number ofdistinct edges terminating on vertex x, whereas the out-degree isthe number of distinct edges originating at x.

Solutions to Selected Exercises in Exercises 10.31. To compute the list of predecessors, Pred(x ), for a vertex x

starting with an adjacency matrix T for a graph G, you can scancolumn x of T and make a list of the vertices y for which T[y, x] = 1.For example, letting L be an initially empty list, you could use thefollowing program strategy:

| /* initialize L to be the empty list */|| for (y = 0; y < MaxVertexNumber; ++ y) {| if (T[y][x] == 1) {

5 | Insert(y, &L); /* insert new member y on list L */| }| }

2. The following is a complete test program for converting the arrayof linked-lists graph representation into the array of bit-vectorsgraph representation. It is executed on the graph of Figure 10.5.

| /*| *| * Solution to Ex. 10.3.2 -- converting a Graph representation from| * -- array of linked lists of successor vertices to an

5 | * -- array of bit vectors representing adjacency sets| *| */|| #include <stdio.h>

10 | #include<stdlib.h>|| #define NumberOfVertices 5| #define ByteSize 8| #define ByteLength (NumberOfVertices/ByteSize +

15 | (((NumberOfVertices % ByteSize) != 0)?1:0))|| typedef struct NodeTag {| int Item;| struct NodeTag *Link;

20 | } NodeType;|| typedef NodeType *ArrayOfLinkedLists[NumberOfVertices];

Page 127: Standish Sol Manual Ece223

Table of Contents — continued

|| typedef char Set[ByteLength];

25 || typedef Set ArrayOfSets[NumberOfVertices];||| void PrintList(NodeType *L)

30 | {| int A;|| printf("(");| while (L != NULL) {

35 | A = L–>Item;| printf("%d",A);| L = L–>Link;| if (L != NULL) printf(", ");| }

40 | printf(")\n");| }|| void InsertNewFirstNode (int A, NodeType **L)| {

45 | NodeType *N;|| N = (NodeType *)malloc(sizeof(NodeType));| N–>Item = A;| N–>Link = *L;

50 | *L = N;| }|| void Insert(int A, int B, ArrayOfLinkedLists F) /* insert A into set B */| { /* in Graph F */

55 | InsertNewFirstNode(A,&F[B–1]);| }|| void PrintLinkedListGraph(ArrayOfLinkedLists F)| {

60 | int i;|| for (i=0; i<NumberOfVertices; ++i){| printf("%2d – ",(i+1));| PrintList(F[i]);

65 | }| }|| int Member(int i, Set S)| {

70 | i – –; /* membership is shifted from 1:n ==> 0:n–1 */| return( ( S[i/ByteSize ] >> (i % ByteSize) ) & 1);| }||

75 | void InitializeSet(Set S)| {| int i;|| for (i=0; i<ByteLength; ++i) S[i]='\0';

80 | }|| void InsertIntoSet(int i, Set S)| {| i – – ; /* insertion is shifted from the number range 1:n ==> 0:n–1 */

Page 128: Standish Sol Manual Ece223

Table of Contents — continued

85 || S[i/ByteSize] |= 1 << (i % ByteSize);| }|| void ConvertSet(NodeType *R, Set S)

90 | {| NodeType *N;| int K;|| N = R;

95 | while (N != NULL) {| K = N–>Item;| N = N–>Link;| InsertIntoSet(K,S);| }

100 | }|| void ConvertGraphRep(ArrayOfLinkedLists F, ArrayOfSets G)| {| int i;

105 || for (i=0; i<NumberOfVertices; ++i) {| InitializeSet(G[i]);| ConvertSet(F[i],G[i]);| }

110 | }|| void PrintSet(Set S)| {| int i;

115 || for (i=1; i<=NumberOfVertices; ++i){| putchar((Member(i,S)?'1':'0'));| putchar(' ');| }

120 | putchar('\n');| }|| void PrintBitVectorGraph(ArrayOfSets G)| {

125 | int i;|| printf(" n –");| for (i=0; i<NumberOfVertices; ++i) printf("%2d",(i+1));| putchar('\n');

130 | for (i=0; i<NumberOfVertices; ++i) {| printf("%2d – ",(i+1)); /* adjust vertex numbers from 0:n–1 ==> 1:n */| PrintSet(G[i]);| }| }

135 || int main(void)| {| int i;| ArrayOfLinkedLists F; /* F is a linked list graph representation */

140 | ArrayOfSets G; /* G is an array of bit-vectors for adjacency sets */|| /* initialize the array of linked lists representation F */| for (i=0; i<NumberOfVertices; ++i) F[i] = NULL;|

145 | /* then load the array of linked lists representation F */| /* with graph of Fig. 10.5 */

Page 129: Standish Sol Manual Ece223

Table of Contents — continued

| Insert(3, 1, F);| Insert(2, 1, F); /* Insert 2 as a new member of the list in F[1] */| Insert(5, 2, F);

150 | Insert(4, 2, F);| Insert(3, 2, F);| Insert(4, 3, F); /* Insert 4 as a new member of the list in F[3] */| Insert(1, 5, F);|

155 | printf("print linked-list representation of the graph of Fig. 10.5, p.413:\n");| PrintLinkedListGraph(F);| putchar('\n');|| ConvertGraphRep(F,G);

160 || printf("print bit-vector representation of the graph of Fig. 10.5, p.413:\n");| PrintBitVectorGraph(G);| }| /* end main */

165 || /* the results printed by the program above are as follows:|| print linked-list representation of the graph of Fig. 10.5, p.413:| 1 - (2, 3)

170 | 2 - (3, 4, 5)| 3 - (4)| 4 - ()| 5 - (1)|

175 | print bit-vector representation of the graph of Fig. 10.5, p.413:| n - 1 2 3 4 5| 1 - 0 1 1 0 0| 2 - 0 0 1 1 1| 3 - 0 0 0 1 0

180 | 4 - 0 0 0 0 0| 5 - 1 0 0 0 0|| */|

Answers to Review Questions 10.41. In depth-first searching in a tree we visit all children of a vertex

before any right brothers and sisters, whereas in breadth-firstsearching in a tree, we visit all right brothers and sisters beforevisiting any children. In depth-first searching in a graph, we visitall previously unvisited neighbors of each neighbor of a vertex xbefore visiting x ’s other neighbors, whereas in breadth-firstsearching in a graph, we visit x ’s unvisited neighbors beforevisiting any unvisited neighbors of those neighbors.

2. Using stacks to hold unvisited neighbors of a vertex in graphsearching yields depth-first searching because, as neighbors of xare popped from the stack and unvisited neighbors of theseneighbors are pushed on the stack, we guarantee that neighbors ofthe neighbors will be processed before other original neighbors of

Page 130: Standish Sol Manual Ece223

Table of Contents — continued

x lower on the stack. Using queues to hold unvisited neighbors ingraph searching results in breadth-first search because allunvisited neighbors of x are processed in arrival order on thequeue before any neighbors of those neighbors which are insertedon the end of the queue after all of the original neighbors.

3. We must ensure that we visit at least the list of those verticesfrom which all others in the graph are accessible via some path inthe graph. In an undirected graph, for instance, we must ensurethat we initially visit at least on vertex in each separateconnected component.

Solutions to Selected Exercises in Exercises 10.4

1. Contents of Queue C List of vertices previously visited at eachstage

(1) –

(2, 3, 4) 1

(3, 4, 5, 6) 1, 2

(4, 5, 6) 1, 2, 3

(5, 6, 7, 8) 1, 2, 3, 4

(6, 7, 8) 1, 2, 3, 4, 5

(7, 8) 1, 2, 3, 4, 5, 6

(8) 1, 2, 3, 4, 5, 6, 7

( ) 1, 2, 3, 4, 5, 6, 7, 8

2. For the solutions to Exs. 10.4.2 and 10.4.3, see the program thatfollows:

| /*| * Solutions to Exs. 10.4.2 and 10.4.3:| *| * Ex. 10.4.2 -- Refinement of Program Strategy 10.7 for Graph Searching

5 | * Ex. 10.4.3 -- Recursive version of Program Strategy 10.7| */|| #include <stdio.h>| #include<stdlib.h>

10 | #include "QueueInterface.h" /* we use Queues as containers */| /* taken from Program 7.19 -- */| /* Circular Queue Representation, on pp. 282-283 */|| #define MaxVertexNumber 9 /* actually one greater than */

15 | /* max vertex number */| /* because we use vertices in 1:n instead of 0:n-1 */|| typedef struct NodeTag {

Page 131: Standish Sol Manual Ece223

Table of Contents — continued

| int VertexNumber;20 | struct NodeTag *Link;

| }Node;|| typedef struct {| Boolean Visited;

25 | Node *AdjacencyList;| }Vertex;|| typedef Vertex ArrayOfVertices[MaxVertexNumber];|

30 || /* ------------------------------------------------------------------ */|| void InsertNewFirstNode (int A, Node **L)| {

35 | Node *N;|| N = (Node *)malloc(sizeof(Node));| N–>VertexNumber = A;| N–>Link = *L;

40 | *L = N;| }|| /* ------------------------------------------------------------------ */|

45 | void ListInsert(int A, int B, ArrayOfVertices F) /* insert A into */| { /* adjacency list */| /* of vertex B */| InsertNewFirstNode(A,&F[B].AdjacencyList); /* in Graph F */| }

50 || /* ------------------------------------------------------------------ */|| void InitGraph(ArrayOfVertices G)| {

55 | int i;|| /* init G to the graph of Figure 10.9 on p. 417 */| for (i=1; i<MaxVertexNumber; ++i) {| G[i].Visited = false;

60 | G[i].AdjacencyList = NULL;| }|| /* G[1] = [2,3,4]; */| ListInsert(4,1,G);

65 | ListInsert(3,1,G);| ListInsert(2,1,G);| /* G[2] = [5,6]; */| ListInsert(6,2,G);| ListInsert(5,2,G);

70 | /* G[3] = []; */| /* G[4] = [7,8]; */| ListInsert(8,4,G);| ListInsert(7,4,G);| /* G[5] = [ ]; */

75 | /* G[6] = [5]; */| ListInsert(5,6,G);| /* G[7] = [1,3,6]; */| ListInsert(6,7,G);| ListInsert(3,7,G);

80 | ListInsert(1,7,G);

Page 132: Standish Sol Manual Ece223

Table of Contents — continued

| /* G[8] = [6]; */| ListInsert(6,8,G);| }|

85 | /* ------------------------------------------------------------------ */|| void Visit(int x)| {| printf("Visit Vertex Number: %2d.\n",x);

90 | }|| /* ------------------------------------------------------------------ */|| void GraphSearch(ArrayOfVertices G, int v) /* Ex. 10.4.2 -- */

100 | { /* refinement of Program Strategy 10.7, p. 416 */| int i,x;| Queue C;| Node *L;|

105 | /* Mark each vertex as being unvisited */| for (i = 1; i<MaxVertexNumber; ++i) {| G[i].Visited = false;| }|

110 | InitializeQueue(&C); /* initialize C to be empty */|| Insert(v,&C); /* let the search begin at vertex v */|| while (!Empty(&C)) {

115 | Remove(&C,&x); /* remove a vertex from the container C */| if (! G[x].Visited) {| Visit(x);| /* mark x as having been visited */| G[x].Visited = true;

120 | /* Enter all unvisited vertices of Vx into C */| L = G[x].AdjacencyList;| while (L != NULL) {| if (! G[L–>VertexNumber].Visited) {| Insert(L–>VertexNumber, &C);

125 | }| L = L–>Link;| }| }| }

130 || }|| /* ------------------------------------------------------------------ */|

135 | /* the following recursive procedure is the solution to Ex. 10.4.3 */|| void RecursiveGraphSearch(ArrayOfVertices A, int v)| {| Node *L;

140 || /* for the recursive function, assume each vertex of */| /* the graph has already been marked unvisited */|| if (!A[v].Visited) {

145 | Visit(v);| A[v].Visited = true;| L = A[v].AdjacencyList;

Page 133: Standish Sol Manual Ece223

Table of Contents — continued

| while (L != NULL) {| if (! A[L–>VertexNumber].Visited) {

150 | RecursiveGraphSearch(A, L–>VertexNumber);| }| L = L–>Link;| }| }

155 || }|| /* ------------------------------------------------------------------ */|

160 | void PrintLinkedListGraph(ArrayOfVertices A)| {| int i,j;| Node *L;|

165 | for (i = 1; i<MaxVertexNumber; ++i) {| L = A[i].AdjacencyList;| printf("G[%2d] = [",i);| while (L != NULL) {| printf("%2d ",L–>VertexNumber);

170 | L = L–>Link;| }| printf("]\n");| }| putchar('\n');

175 | }|| /* ------------------------------------------------------------------ */|| int main(void)

180 | {| int i;| ArrayOfVertices G; /* G is an array of vertices */| /* each vertex of which has */| /* a linked adjacency list */

185 | InitGraph(G);|| printf("print the graph of Figure 10.9 on p. 417:\n");| PrintLinkedListGraph(G);|

190 | printf("Search graph and print list of vertices visited.\n");| GraphSearch(G,1); /* GraphSearch uses queue containers */| /* to test breadth-first search */|| /* comment GraphSearch(G,1) above, and then uncomment */

195 | /* RecursiveGraphSearch(G,1) below */| /* to test RecursiveGraphSearch */|| /* RecursiveGraphSearch(G,1); */|

200 | }| /* end main */|| /* The following is the printout from the program above:|

205 | print the graph of Figure 10.9 on p. 417:| G[ 1] = [ 2 3 4 ]| G[ 2] = [ 5 6 ]| G[ 3] = [ ]| G[ 4] = [ 7 8 ]

Page 134: Standish Sol Manual Ece223

Table of Contents — continued

210 | G[ 5] = [ ]| G[ 6] = [ 5 ]| G[ 7] = [ 1 3 6 ]| G[ 8] = [ 6 ]|

215 | Search graph and print list of vertices visited.| Visit Vertex Number: 1.| Visit Vertex Number: 2.| Visit Vertex Number: 5.| Visit Vertex Number: 6.

220 | Visit Vertex Number: 3.| Visit Vertex Number: 4.| Visit Vertex Number: 7.| Visit Vertex Number: 8.| */

3. See lines 133:154 of the program given above in Ex. 10.4.2 for thesolution to Ex. 10.4.3.

4. All you have to do is to remove the initialization code (on lines8:10) from Program Strategy 10.7 and make the removed lines intoa separate procedure, called, say, MarkVerticesUnvisited(G), followingwhich, you insert a call on MarkVerticesUnvisited(G) on line 4 of ProgramStrategy 10.10, and then change line 6 of Program Strategy 10.10to be: “if (! v.Visited) GraphSearch(G,v);”

Answers to Review Questions 10.51. A DAG (directed acyclic graph) is a directed graph having no

cycles. [Recall that a cycle in a directed graph is a path with two ormore distinct vertices that forms a loop.]

2. Let G be a DAG. A topological order for the vertices of G is a list Lof G’s vertices such that if there is a path from vertex v to vertexw in G, then v comes before w in the vertex list L. Another way ofsaying this is that L is a vertex list for G in which, for each vertexv, all predecessors of v are listed in L before v itself is listed.

3. First compute the in-degrees of each vertex v and store them,say, in an array D[v] indexed by vertices of the graph G. Then, usingan initially empty queue Q, scan the array D and insert in Q allvertices having in-degrees of 0. These are the vertices with nopredecessors in G which can be listed first in topological order.Now let L be an empty list. Process the vertices v in the queue Qone at a time until Q becomes empty. For each vertex v removedfrom the front of Q, add v to the end of list L, decrease the in-degree of each successor w of v by one, and if the in-degree of wdrops to zero, insert w on the rear of Q. When Q becomes empty, Lcontains a list of vertices of G in topological order.

Solutions to Selected Exercises in Exercises 10.5

Page 135: Standish Sol Manual Ece223

Table of Contents — continued

1. See the function TopologicalOrder(G, L) on lines 82:138 of the followingprogram:

| /*| * Solution to Exs. 10.5.1 and 10.5.3| *| * Ex. 10.5.1 -- Implement Program Strategy 10.12 for TopologicalOrder

5 | * Ex. 10.5.3 -- Recursive version of Topological Order| */|| #include <stdio.h>| #include<stdlib.h>

10 | #include "QueueInterface.h" /* we use Queues as containers */| /* taken from Program 7.19 -- */| /* Circular Queue Representation, on pp. 282-283 */|| #define MaxVertexNumber 6 /* actually one greater than max vertex */

15 | /* number because we use vertices in 1:n instead of 0:n-1 */|| typedef struct NodeTag {| int VertexNumber;| struct NodeTag *Link;

20 | }Node;|| typedef struct {| int InDegree;| Node *AdjacencyList;

25 | }Vertex;|| typedef Vertex ArrayOfVertices[MaxVertexNumber];||

30 | Queue TopoOrderList; /* the output list is represented */| /* by a queue for simplicity */|| /* ------------------------------------------------------------------ */|

35 | void InsertNewFirstNode (int A, Node **L)| {| Node *N;|| N = (Node *)malloc(sizeof(Node));

40 | N–>VertexNumber = A;| N–>Link = *L;| *L = N;| }|

45 | /* ------------------------------------------------------------------ */|| void ListInsert(int A, int B, ArrayOfVertices F) /* insert A into */| { /* adjacency list of vertex B */| InsertNewFirstNode(A,&F[B].AdjacencyList); /* in Graph F */

50 | }|| /* ------------------------------------------------------------------ */|| void InitGraph(ArrayOfVertices G)

55 | {| int i;|| /* init G to the graph of Figure 10.11 on p. 420 */

Page 136: Standish Sol Manual Ece223

Table of Contents — continued

| for (i=1; i<MaxVertexNumber; ++i) {60 | G[i].InDegree = 0;

| G[i].AdjacencyList = NULL;| }|| /* G[1] = [2,3,4,5]; */

65 | ListInsert(5,1,G);| ListInsert(4,1,G);| ListInsert(3,1,G);| ListInsert(2,1,G);| /* G[2] = [4]; */

70 | ListInsert(4,2,G);| /* G[3] = [2,5]; */| ListInsert(5,3,G);| ListInsert(2,3,G);| /* G[4] = [5]; */

75 | ListInsert(5,4,G);| }|| /* ------------------------------------------------------------------ */|

80 | /* the following procedure is the solution to Ex. 10.5.1 */|| void TopologicalOrder(ArrayOfVertices G, Queue *TopoOrderList)| {| int x,w; /* vertices are item types for use in queues */

85 | Queue Q;| Node *L;|| /* Compute the in-degrees A[x].InDegree of the */| /* vertices x in the graph G */

90 || /* initialize InDegrees to zero */| for (x=1; x<MaxVertexNumber; ++x) G[x].InDegree = 0;|| for (x=1; x<MaxVertexNumber; ++x) {

95 | L = G[x].AdjacencyList;| while (L != NULL) {| w = L–>VertexNumber;| G[w].InDegree++;| L = L–>Link;

100 | }| }|| /* Initialize the queue Q to contain all vertices */| /* having zero in-degrees */

105 | InitializeQueue(&Q); /* initialize Q to be the empty queue */| for(x=1; x<MaxVertexNumber; ++x) {| if (G[x].InDegree == 0) {| Insert(x,&Q); /* insert x on the rear of Q*/| } /* if x's InDegree == 0 */

110 | }|| /* Initialize the list TopoOrderList to be the empty list */| InitializeQueue(TopoOrderList); /* for simplicity, */| /* TopoOrderList is */

115 | /* represented by a queue. */|| /* Process vertices in the queue Q until the queue becomes empty */| while (!Empty(&Q)) {|

120 | Remove(&Q,&x); /* Remove vertex x from the front of Q */

Page 137: Standish Sol Manual Ece223

Table of Contents — continued

|| Insert(x,TopoOrderList); /* Insert x on the rear of */| /* list TopoOrderList */|

125 | /* for each successor w of x, where w in Vx do begin */| L = G[x].AdjacencyList;| while (L != NULL) {| w = L–>VertexNumber;| G[w].InDegree– –; /* decrease predecessor */

130 | /* count of w */| if (G[w].InDegree == 0) Insert(w,&Q); /* insert w */| /* on the rear of Q */| L = L–>Link;| }

135 | }|| }| /* TopologicalOrder */|

140 | /* ------------------------------------------------------------------ */|| void ComputeInDegrees(ArrayOfVertices G)| {| int x,w; /* vertices are item types for use in queues */

145 | Node *L;|| /* Compute the in-degrees A[x].InDegree of the */| /* vertices x in the graph G */|

150 | /* initialize InDegrees to zero */| for (x=1; x<MaxVertexNumber; ++x) G[x].InDegree = 0;|| for (x=1; x<MaxVertexNumber; ++x) {| L = G[x].AdjacencyList;

155 | while (L != NULL) {| w = L–>VertexNumber;| G[w].InDegree++;| L = L–>Link;| }

160 | }| }|| /* ------------------------------------------------------------------ */|

165 | /* the following recursive procedure is the solution to Ex. 10.5.3 */|| void RecursiveTopoOrder(ArrayOfVertices G, int v)| {| Node *L;

170 || if (G[v].InDegree == 0) Insert(v, &TopoOrderList);|| L = G[v].AdjacencyList;|

175 | while (L != NULL) {| G[L–>VertexNumber].InDegree– –;| RecursiveTopoOrder(G,L–>VertexNumber);| L = L–>Link;| }

180 || }|

Page 138: Standish Sol Manual Ece223

Table of Contents — continued

| /* ------------------------------------------------------------------ */|

185 | void PrintLinkedListGraph(ArrayOfVertices A)| {| int i,j;| Node *L;|

190 | for (i = 1; i<MaxVertexNumber; ++i) {| L = A[i].AdjacencyList;| printf("G[%2d] = [",i);| while (L != NULL) {| printf("%2d ",L–>VertexNumber);

195 | L = L–>Link;| }| printf("]\n");| }| putchar('\n');

200 | }|| /* ------------------------------------------------------------------ */|| void PrintQueue(Queue *Q)

205 | {| ItemType x;|| putchar('(');| while (!Empty(Q)) {

210 | Remove(Q,&x);| printf("%2d ",x);| }| printf(")\n");| }

215 || /* ------------------------------------------------------------------ */|| int main(void)| {

220 | int i;| ArrayOfVertices G; /* G is a linked list graph representation */|| InitGraph(G);|

225 | printf("print the graph of Figure 10.11 on p. 420:\n");| PrintLinkedListGraph(G);|| /* make a list of the vertices of G in topological order */| InitializeQueue(&TopoOrderList);

230 | TopologicalOrder(G, &TopoOrderList);|| /* comment out function calls above and comment in these three */| /* InitializeQueue(&TopoOrderList); */| /* ComputeInDegrees(G); */ /* Compute the*/

235 | /* InDegrees of the Graph G */| /* RecursiveTopoOrder(G,1); */ /* This function */| /* assumes InDegrees are already computed */| /* and that the TopoOrderList is initialized to empty */|

240 | printf("The list of vertices in topological order is = \n");| PrintQueue(&TopoOrderList); /* Print the list of vertices */| /* in topological order */|| }

Page 139: Standish Sol Manual Ece223

Table of Contents — continued

245 | /* end main */|| /* The print out of the above program is as follows:|| print the graph of Figure 10.11 on p. 420:

250 | G[ 1] = [ 2 3 4 5 ]| G[ 2] = [ 4 ]| G[ 3] = [ 2 5 ]| G[ 4] = [ 5 ]| G[ 5] = [ ]

255 || The list of vertices in topological order is =| ( 1 3 2 4 5 )|| */

2. If, after the procedure of Program Strategy 10.12 terminates,there are any vertices in the graph whose in-degree has notdropped to zero and which are not listed in topological order, thensuch vertices lie on an illegal cycle in the graph.

3. See the function RecursiveTopoOrder( ) on lines 167:181 of the programgiven in Ex. 10.5.1 above.

4. Let G = (V,E) be a graph of n vertices and e edges. To compute in-degrees takes time O(e + n) because n vertices are scanned tohave their in-degrees initialized to zero, followed by scanningeach of the e edges in E to increment the in-degrees of theirtermini. Then the n vertices are scanned to put on queue Q allvertices with in-degree 0. Finally, each of the e edges in G is againscanned while finding successors of each vertex whose in-degreehas dropped to zero and has been processed in the queue Q. Thetotal time required is thus O(e + n).

Answers to Review Questions 10.61. A weighted graph is a directed graph in which positive numerical

quantities called weights are attached to the edges. A shor testpath between two vertices v and w in a weighted graph G is a pathfrom v to w in G, the sum of whose edge weights is less than orequal to the sum of the edge weights on any other distinct pathfrom v  to w .

2. Dijkstra’s shortest path algorithm gives a way to compute theshortest distance from a single chosen vertex v to every othervertex w in a weighted directed graph G. The basic idea is to startwith a subset W = {v} of the vertices V of graph G, and to enlarge itin steps, adding at each step a new vertex w ∈ V – W which is at aminimum distance from v among all the vertices in V – W that havenot yet been added to W. Eventually W is enlarged to contain all

Page 140: Standish Sol Manual Ece223

Table of Contents — continued

vertices of V and the shortest distance from v to every othervertex in V then becomes known.

Solutions to Selected Exercises in Exercises 10.61. The function ShortestPath(void) given on lines 171:230 below provides

a refinement of Program Strategy 10.16.

| /*| * Solution to Ex. 10.6.1 -- ShortestPaths| */|

5 | #include <stdio.h>| #include<stdlib.h>|| #define MaxVertex 6| #define Infinity 32000 /* it can't be INT_MAX, since adding small edge */

10 | /* lengths to INT_MAX causes it to wrap to a */| /* big negative number. */|| #define NumberOfVertices 7 /* one more than MaxVertex */| /* because we start at 1 not 0 */

15 | #define ByteSize 8| #define ByteLength (NumberOfVertices/ByteSize +| (((NumberOfVertices % ByteSize) != 0)?1:0))|| typedef int Weight;

20 || typedef int Vertex;|| typedef Weight AdjacencyMatrix[NumberOfVertices][NumberOfVertices];|

25 | typedef char Set[ByteLength];||| AdjacencyMatrix T;| Set W;

30 | Weight ShortestDistance[NumberOfVertices];||| /* -------------------------------------------------------------- */|

35 | int Member(int i, Set S)| {| i – –; /* membership is shifted from 1:n ==> 0:n–1 */| return ( ( S[i/ByteSize ] >> (i % ByteSize) ) & 1);| }

40 || /* -------------------------------------------------------------- */|| void InitializeSet(Set S)| {

45 | int i;|| for (i=0; i<ByteLength; ++i) S[i]='\0';| }|

50 | /* -------------------------------------------------------------- */|

Page 141: Standish Sol Manual Ece223

Table of Contents — continued

| void InsertIntoSet(int i, Set S)| {| i – –; /* insertion is shifted from the number */

55 | /* range 1:n to the range 0:n–1 */|| S[i/ByteSize] |= 1 << (i % ByteSize);| }|

60 | /* -------------------------------------------------------------- */|| void PrintSet(Set S)| {| int i;

65 || for (i=1; i<=MaxVertex; ++i){| putchar((Member(i,S)?'1':'0'));| putchar(' ');| }

70 | putchar('\n');| }|| /* -------------------------------------------------------------- */|

75 | /* Boolean Full(Set S) returns true iff set S == the whole vertex set V. */| /* Note: this function is inefficient and should later be replaced by a*/| /* test against a "full" bit-vector. For example, "return (S[0] == '\077');" */| /* suffices in the case of the current one-byte bit-vector example. */|

80 | Boolean Full(Set S)| {| int i;| Boolean result;|

85 | result = true;|| for (i=1; i<=MaxVertex; ++i){| if (!Member(i,W)) {| result = false;

90 | break;| }| }|| return result;

95 | }|| /* -------------------------------------------------------------- */|| void InitializeAdjacencyMatrix(AdjacencyMatrix T)

100 | {| int i,j;|| for (i=1; i<=MaxVertex; ++i) {| for (j=1; j<=MaxVertex; ++j) {

105 | T[i][j] = Infinity;| }| }|| for (i=1; i<=MaxVertex; ++i) T[i][i] = 0;

110 || T[1][2] = 3; T[1][6] = 5;| T[2][3] = 7; T[2][6] = 10;| T[3][4] = 5; T[3][5] = 1;

Page 142: Standish Sol Manual Ece223

Table of Contents — continued

| T[4][5] = 6;115 | T[5][6] = 7;

| T[6][4] = 2; T[6][3] = 8;|| }|

120 | /* -------------------------------------------------------------- */|| void PrintAdjacencyMatrix(AdjacencyMatrix T)| {| int i,j;

125 || printf("---");| for (i=1; i<=MaxVertex; ++i) printf("%3d",i);| putchar('\n');| for (i=1; i<=MaxVertex; ++i) {

130 | printf("%3d",i);| for (j=1; j<=MaxVertex; ++j) {| if (T[i][j] == Infinity) {| printf(" •");| } else {

135 | printf("%3d",T[i][j]);| }| }| putchar('\n');| }

140 || putchar('\n');| putchar('\n');|| }

145 || /* -------------------------------------------------------------- */|| void PrintDistances(void)| {

150 | int i;|| printf("Shortest Distances to Vertices in Graph G\n");| for (i=1; i<=MaxVertex; ++i) {| printf("vertex %2d: %5d\n",i, ShortestDistance[i]);

155 | }| }|| /* -------------------------------------------------------------- */|

160 | Weight Minimum(Weight x, Weight y)| {| if (x < y) {| return x;| } else {

165 | return y;| }| }|| /* -------------------------------------------------------------- */

170 || void ShortestPath(void)| {|| /* Let MinDistance be a variable that contains edge weights */

175 | /* as values and let Minimum(x,y) be a function whose value */

Page 143: Standish Sol Manual Ece223

Table of Contents — continued

| /* is the lesser of x and y */|| Vertex v1,u1,w1,v2;| Weight MinDistance;

180 ||| /* Let v1 in V be the origin vertex at which the shortest path starts */| /* Initialize W and ShortestDistance[u] as follows */|

185 | /* in the comments that follow V is the entire vertex set */|| v1 = 1; /* set up the origin vertex */|| /* W = [v1]; */

190 | InitializeSet(W);| InsertIntoSet(v1,W);|| ShortestDistance[v1] = 0;|

195 | for (v2 = 1; v2<=MaxVertex; ++v2) { /* for each v2 in V – [v1] */| if (!Member(v2,W)) ShortestDistance[v2] = T[v1][v2];| }|| /* PrintDistances(); */

200 || /* Now repeatedly enlarge W until W includes all vertices in V */|| while (!Full(W)) { /* while (W != V) { */| /* find the vertex v2 in V – W of minimum distance from v1 */

205 | MinDistance = Infinity;| for (w1=1; w1<=MaxVertex; ++w1) { /* for each w1 in V – W */| if (!Member(w1,W)) { /* if w1 in (V – W) */| if (ShortestDistance[w1] < MinDistance) {| MinDistance = ShortestDistance[w1];

210 | v2 = w1;| }| }| }|

215 | /* add v2 to W by doing W = W + [v2]; */| InsertIntoSet(v2,W);| printf("new vertex to add to W = %2d\n",v2);|| /* update the shortest distances to vertices in V – W */

220 | for (u1=1; u1<=MaxVertex; ++u1) {/* for each u1 in V – W*/

| if (!Member(u1,W)) {| ShortestDistance[u1] =| Minimum(ShortestDistance[u1],| ShortestDistance[v2] + T[v2][u1]);

225 | }| }| } /* end while */|| }

230 | /* end ShortestPath */|| /* -------------------------------------------------------------- */|| int main(void)

235 | {|

Page 144: Standish Sol Manual Ece223

Table of Contents — continued

| /* Initialization */| InitializeAdjacencyMatrix(T);| printf("The adjacency matrix for graph G is:\n");

240 | PrintAdjacencyMatrix(T);|| /* Find Shortest Path */| ShortestPath();|

245 | /* Print the ShortestDistances */| putchar('\n');| PrintDistances();|| PrintSet(W);

250 || }| /* end main program */|| /* the printout obtained from running the above program is:

255 || The adjacency matrix for graph G is:| --- 1 2 3 4 5 6| 1 0 3 • • • 5| 2 • 0 7 • • 10

260 | 3 • • 0 5 1 •| 4 • • • 0 6 •| 5 • • • • 0 7| 6 • • 8 2 • 0

265 ||| new vertex to add to W = 2| new vertex to add to W = 6| new vertex to add to W = 4

270 | new vertex to add to W = 3| new vertex to add to W = 5|| Shortest Distances to Vertices in Graph G| vertex 1: 0

275 | vertex 2: 3| vertex 3: 10| vertex 4: 7| vertex 5: 11| vertex 6: 5| */

2. We have initially, that S = ∑(2≤ i≤n) ∑ (1≤ j≤n+ 1– i) C, where C is a

constant and we want to show that S = C * n(n–1)

2 , so that S is O(n2).

First, recall that the number of integers in the range a:b = { j | a ≤ j≤ b } is b – a + 1 = top – bottom + 1, so we have ∑(1≤ j≤n+1– i) C =C*(n + 1 – i).

Hence,

S = C * ∑(2≤i≤n) [(n + 1) – i]

= C * [ ∑(1≤i≤n) [(n + 1) – i] – n ]

= C * [ n*(n + 1) – n(n+1)

2 – n ]

Page 145: Standish Sol Manual Ece223

Table of Contents — continued

= C * [ n(n+1)

2 – 2n2 ]

= C * [ n 2 + n  – 2n

2 ]

= C * [ n2 – n

2 ]

= C * [ n (n   –   1 )

2 ] , which is what we

wanted to show.

Answers to Review Questions 10.71. A task network is a directed acyclic graph (DAG) whose nodes

represent tasks and have task durations attached. A PERT Chart isa representation of a task network showing, for each task, itspredecessors, its successors, and its duration. A milestone chartis a view (or representation) of a task network showing for eachtask its earliest start time, latest finishing time, and slack time(if any). Certain key times on the critical path are presented asmilestones.

2. A critical path in a task network G is a path from the start node ofG to the finish node of G such that if any task on the critical pathhas its completion time delayed by an amount of time ∆T, then thecompletion time for the entire project is delayed by ∆T .

3. Slack time is the time duration available for delaying a task’sindividual completion time that can occur without delaying theentire project completion time. Only tasks that are not on thecritical path can have such slack time.

4. Tasks on the critical path have no slack time because theircompletion times cannot be delayed without delaying the projectcompletion time.

Solutions to Selected Exercises in Exercises 10.71. Complete implementations of Program Strategies 10.24, 10.25,

10.27 and 10.28 are given in the following complete C program.However, the arrays used in this program are not indexed byindices in the range 0:n–1 as called for in Ex. 10.7.1. In contrast,indices in the range 0:n are used where n is the maximum vertexnumber of a vertex in the example of Figure 10.19. This sidestepsan index range translation that would have rendered the programbelow less legible.

| /*

Page 146: Standish Sol Manual Ece223

Table of Contents — continued

| * Solution to Ex. 10.7.1 -- Critical Path Algorithms| */|

5 | #include <stdio.h>| #include <stdlib.h>| #include "QueueInterface.h" /* use Queue ADT, see p. 262 and */| /* pp 282–283 */|

10 | #define MaxVertex 12|| /* Actually MaxVertex == 1 + maximum vertex number used, in order */| /* to accommodate arrays indexed by 1:n instead of 0:n–1 */|

15 |||| typedef int Duration;|

20 | typedef int VertexNumber;|| typedef Duration DurationArray[MaxVertex];|| typedef int AdjacencyMatrix[MaxVertex][MaxVertex];

25 || AdjacencyMatrix T;| DurationArray D,EFT,EST,LFT,LST;| Duration PFT;| VertexNumber TopoOrderArray[MaxVertex];

30 ||| /* -------------------------------------------------------------- */|| void InitializeDurationArray(DurationArray D)

35 | {| D[1] = 0;| D[2] = 5;| D[3] = 9;| D[4] = 20;

40 | D[5] = 2;| D[6] = 3;| D[7] = 10;| D[8] = 15;| D[9] = 2;

45 | D[10] = 10;| D[11] = 0;| }|| /* -------------------------------------------------------------- */

50 || void InitializeAdjacencyMatrix(AdjacencyMatrix T)| {| int i,j;|

55 | for (i = 1; i<MaxVertex; ++i) {| for (j = 1; j<MaxVertex; ++j) {| T[i][j] = 0;| }| }

60 ||| T[1][2] = 1; T[1][4] = 1;| T[2][3] = 1;

Page 147: Standish Sol Manual Ece223

Table of Contents — continued

| T[3][5] = 1;65 | T[4][5] = 1;

| T[5][6] = 1; T[5][7] = 1; T[5][8] = 1;| T[6][9] = 1;| T[7][9] = 1;| T[8][9] = 1;

70 | T[9][10] = 1;| T[10][11] = 1;|| }|

75 | /* -------------------------------------------------------------- */|| void PrintAdjacencyMatrix(AdjacencyMatrix T)| {| int i,j;

80 || printf("The Adjacency Matrix is:\n\n");| printf("---");| for (i = 1; i<MaxVertex; ++i) printf("%3d",i);| putchar('\n');

85 | for (i = 1; i<MaxVertex; ++i) {| printf("%3d",i);| for (j = 1; j<MaxVertex; ++j) {| printf("%3d",T[i][j]);| }

90 | putchar('\n');| }|| putchar('\n');| putchar('\n');

95 || }|| /* -------------------------------------------------------------- */|

100 | void PrintDurationArray(DurationArray D)| {| int i;|| printf(" i = ");

105 | for (i = 1; i<MaxVertex; ++i) printf("%3d",i);| putchar('\n');| printf(" A[i] = ");| for (i = 1; i<MaxVertex; ++i) printf("%3d",D[i]);| putchar('\n');

110 | }|| /* -------------------------------------------------------------- */|| void PrintTopoOrderArray(void)

115 | {| int i;|| printf(" i = ");| for (i = 1; i<MaxVertex; ++i) printf("%3d",i);

120 | putchar('\n');| printf("TopoOrder = ");| for (i = 1; i<MaxVertex; ++i) printf("%3d",TopoOrderArray[i]);| putchar('\n');| }

125 |

Page 148: Standish Sol Manual Ece223

Table of Contents — continued

| /* -------------------------------------------------------------- */|| void PrintQueue(Queue *Q)| {

130 | ItemType x;|| putchar('(');| while (!Empty(Q)) {| Remove(Q,&x);

135 | printf("%2d ",x);| }| printf(")\n");| }|

140 | /* -------------------------------------------------------------- */|| void TopologicalOrder(void)| {| DurationArray InDegree;

145 | ItemType x,w,i,j; /* vertices are item types for use in queues */| Queue Q,L;|| /* Compute the in-degrees InDegree[x] of the vertices x in G */| for (x=1; x<MaxVertex; ++x) InDegree[x] = 0; /* initialize */

150 | /* InDegree[x] to zero */| for (x=1; x<MaxVertex; ++x) {| for (w=1; w<MaxVertex; ++w) InDegree[x] += T[w][x];| }|

155 | /* Initialize the queue Q to contain all vertices */| /* having zero in-degrees */| InitializeQueue(&Q); /* initialize Q to be the empty queue */| for (x=1; x<MaxVertex; ++x) {| if (InDegree[x] == 0) Insert(x,&Q); /* insert x */

160 | /* on the rear of Q if InDegree[x] = 0 */| }|| /* Initialize the list L to be the empty list */| InitializeQueue(&L); /* for simplicity, we let the list L */

165 | /* be represented by a queue, also. */|| /* Process vertices in the queue Q until the */| /* queue becomes empty */| while (!Empty(&Q)) {

170 | Remove(&Q,&x); /* Remove vertex x from front of Q */| Insert(x,&L); /* Insert x on rear of list L */|| for (w=1; w<MaxVertex; ++w) { /* for each successor w */| /* of x, where w in Vx */

175 | if (T[x][w] == 1) { /* i.e., if w is a successor of x */| InDegree[w] – –; /* decrease predecessor */| /* count of w */| if (InDegree[w] == 0) {| Insert(w,&Q); /* insert w */

180 | } /* on the rear of Q */| }| }| }|

185 | /* The list L now contains the vertices of G in topological order */| /* Remove them one by one and put them into TopoOrderArray. */| for (i=1; i<MaxVertex; ++i) {

Page 149: Standish Sol Manual Ece223

Table of Contents — continued

| Remove(&L,&x);| TopoOrderArray[i] = x;

190 | }|| }| /* end TopologicalOrder */|

195 | /* -------------------------------------------------------------- */|| Boolean EmptyPred(VertexNumber v)| {| VertexNumber w;

200 | Boolean result;|| result = true;|| for (w=1; w<MaxVertex; ++w) {

205 | if (T[w][v] == 1) { /* w is a predecessor of v */| result = false;| break;| }| }

210 || return result;| }||

215 | /* -------------------------------------------------------------- */|| void ComputeEarliestFinishTimes(void)| {| VertexNumber i,v,w;

220 || for (i=1; i<MaxVertex; ++i) {| v = TopoOrderArray[i]; /* let v be the ith vertex */| /* in topological order */| if (EmptyPred(v)) { /* v has no predecessors */

225 | EFT[v] = D[v];| } else {| EFT[v] = 0;| for (w=1; w<MaxVertex; ++w) { /* w in Pred(v) */| if (T[w][v] == 1) { /* if w is a predecessor of v */

230 | if (EFT[w] + D[v] > EFT[v]) {| EFT[v] = EFT[w] + D[v];| }| }| }

235 | }| }|| }|

240 | /* -------------------------------------------------------------- */|| void ComputeProjectFinishTime(Duration *PFT)| {| VertexNumber v;

245 || /* The earliest Project Finish Time, PFT, is the latest of the */| /* earliest finishing times for any of the vertices in the graph. */| *PFT = 0;| for (v=1; v<MaxVertex; ++v) {

Page 150: Standish Sol Manual Ece223

Table of Contents — continued

250 | if (EFT[v] > *PFT) *PFT = EFT[v];| }| }|| /* -------------------------------------------------------------- */

255 || Boolean EmptySucc(VertexNumber v)| {| VertexNumber w;| Boolean result;

260 || result = true;|| for (w=1; w<MaxVertex; ++w) {| if (T[v][w] == 1) { /* w is a successor of v */

265 | result = false;| break;| }| }| return result;

270 | }|| /* -------------------------------------------------------------- */|| void ComputeLatestFinishTimes(void)

275 | {| VertexNumber i,v,w;|| for (i=MaxVertex; i>=1; – – i) {| v = TopoOrderArray[i]; /* let v be the ith vertex */

280 | /* in reverse topological order */| if (EmptySucc(v)) { /* if v has no successors */| LFT[v] = PFT;| } else {| LFT[v] = PFT;

285 | for (w=1; w<MaxVertex; ++w) { /* w in Succ(v) */| if (T[v][w] == 1) { /* w is a successor of v */| if (LFT[w] – D[w] < LFT[v]) {| LFT[v] = LFT[w] – D[w];| }

290 | }| }| }| }|

295 | }| /* ComputeLatestFinishTimes */|| /* -------------------------------------------------------------- */|

300 | void ComputeLatestStartTimes(void)| {| VertexNumber v;|| for (v=1; v<MaxVertex; ++v) LST[v] = LFT[v] – D[v];

305 || }|| /* -------------------------------------------------------------- */|

310 | void ComputeEarliestStartTimes(void)| {

Page 151: Standish Sol Manual Ece223

Table of Contents — continued

| VertexNumber v;|| for (v=1; v<MaxVertex; ++v) EST[v] = EFT[v] – D[v];

315 || }|| /* -------------------------------------------------------------- */|

320 | void PrintVerticesOnCriticalPath(void)| {| VertexNumber v;|| printf("Vertices on Critical Path = \n");

325 | for (v=1; v<MaxVertex; ++v) {| if (EST[v] == LST[v]) printf(" %3d",v);| }| }|

330 | /* -------------------------------------------------------------- */|| int main(void) /* MainProgram */| {| /* Note: the following are declared globally:

335 | AdjacencyMatrix T;| DurationArray D,EFT,EST,LFT,LST;| Duration PFT;| VertexNumber TopoOrderArray[MaxVertex];| */

340 ||| /* Initialization */| InitializeAdjacencyMatrix(T);| /* PrintAdjacencyMatrix(T); */

345 | InitializeDurationArray(D);| /* printf("Duration Array:\n"); */| /* PrintDurationArray(D); putchar('\n'); */|| /* Compute Topological Order */

350 | TopologicalOrder();| PrintTopoOrderArray(); putchar('\n');|| /* Compute Critical Path & Other Times in Activity Network */| ComputeEarliestFinishTimes();

355 | ComputeProjectFinishTime(&PFT);| ComputeLatestFinishTimes();| ComputeLatestStartTimes();| ComputeEarliestStartTimes();|

360 | /* Print Results */| printf("Earliest Finish Times:\n");| PrintDurationArray(EFT); putchar('\n');| printf("Project Finish Time, PFT = %d\n",PFT);| printf("Latest Finish Times:\n");

365 | PrintDurationArray(LFT); putchar('\n');| printf("Latest Start Times:\n");| PrintDurationArray(LST); putchar('\n');| printf("Earliest Start Times:\n");| PrintDurationArray(EST); putchar('\n');

370 || /* Print vertices on Critical Path */| PrintVerticesOnCriticalPath();|

Page 152: Standish Sol Manual Ece223

Table of Contents — continued

| }375 | /* main program */

|| /* the printout obtained from running the above program is:|| The Adjacency Matrix is:

380 || --- 1 2 3 4 5 6 7 8 9 10 11| 1 0 1 0 1 0 0 0 0 0 0 0| 2 0 0 1 0 0 0 0 0 0 0 0| 3 0 0 0 0 1 0 0 0 0 0 0

385 | 4 0 0 0 0 1 0 0 0 0 0 0| 5 0 0 0 0 0 1 1 1 0 0 0| 6 0 0 0 0 0 0 0 0 1 0 0| 7 0 0 0 0 0 0 0 0 1 0 0| 8 0 0 0 0 0 0 0 0 1 0 0

390 | 9 0 0 0 0 0 0 0 0 0 1 0| 10 0 0 0 0 0 0 0 0 0 0 1| 11 0 0 0 0 0 0 0 0 0 0 0|| Duration Array:

395 | i = 1 2 3 4 5 6 7 8 9 10 11| A[i] = 0 5 9 20 2 3 10 15 2 10 0|| i = 1 2 3 4 5 6 7 8 9 10 11| TopoOrder = 1 2 4 3 5 6 7 8 9 10 11

400 || Earliest Finish Times:| i = 1 2 3 4 5 6 7 8 9 10 11| A[i] = 0 5 14 20 22 25 32 37 39 49 49|

405 | Project Finish Time, PFT = 49| Latest Finish Times:| i = 1 2 3 4 5 6 7 8 9 10 11| A[i] = 0 11 20 20 22 37 37 37 39 49 49|

410 | Latest Start Times:| i = 1 2 3 4 5 6 7 8 9 10 11| A[i] = 0 6 11 0 20 34 27 22 37 39 49|| Earliest Start Times:

415 | i = 1 2 3 4 5 6 7 8 9 10 11| A[i] = 0 0 5 0 20 22 22 22 37 39 49|| Vertices on Critical Path =| 1 4 5 8 9 10 11| */

2. Two noncritical tasks share the same slack time if they are “inseries” (i.e., if one task either precedes or follows the other alonga path), whereas two noncritical tasks are independent if they arein “parallel” (i.e., if they are on separate paths or path segmentssuch that neither precedes or follows the other). Independentnoncritical tasks have independent slack times that do not need tobe shared. A slack time ∆ t shared between two tasks t1 and t2

means that if t1 uses a portion ∆u of ∆ t to delay its completiontime, then t2 cannot delay its completion time by more than ∆t – ∆uwithout delaying the project completion time. That is, shared slack

Page 153: Standish Sol Manual Ece223

Table of Contents — continued

time ∆u used by one task diminishes the slack time available tothe other task by an identical amount ∆u.

Answers to Review Questions 10.81. If G is a weighted, connected, undirected graph, and T is a subgraph

of G containing all vertices of G and some of the edges of G suchthat T is a tree, then T is said to be a spanning tree for G. If thecost of a spanning tree is defined to be the sum of the edgeweights for edges in T, then a minimal spanning tree for G is aspanning tree of least cost among all spanning trees of G.

2. A flow network is a connected graph having flow capacitiesattached to its edges (as edge weights) and having two distinctvertices designated as the source and the destination (or the sink)respectively. A maximum flow from the source to the destinationcan be found by determining a minimum cut. A cut is a set of edgeswhich, when removed from the flow network, separates thenetwork into two distinct connected components, one of whichcontains the source and the other of which contains thedestination. A cut is a minimum cut if the sum of the flowcapacities of its edges is less than or equal to that of any othercut.

3. The four-color problem is the problem of assigning one of fourdistinct colors to each vertex in a planar graph so that no twoadjacent vertices have the same assigned color. It is equivalent tocoloring the countries on a map using four colors so that no twocountries sharing a common border have the same color. The four-color problem was solved in 1977 by Appel and Haken.

4. NP stands for the class of problems that can be solved inpolynomial time on a nondeterministic computer. To say that aproblem is in NP means that you can give an algorithm for solvingit that runs in polynomial time on a non-deterministic computer. Aproblem is NP-complete if it is in NP and if any other problem inNP can be translated into it in polynomial time. The question “P =NP?” asks whether or not the class of problems NP is the same asthe class of problems P, where P is the class of problems that canbe solved in polynomial time on a deterministic machine.

5. O(cn). The best known time for solving any NP-complete problemon a deterministic machine is exponential.

6. If someone were to prove that P = NP then it would be possible tosolve problems in NP on deterministic machines in polynomialtime instead of exponential time.

Page 154: Standish Sol Manual Ece223

Table of Contents — continued

7. A Hamiltonian circuit in a graph G is a cycle that travels througheach vertex of G exactly once.

8. If G is a weighted, directed graph, the traveling salespersonproblem (TSP) is that of determining a cycle of least cost in G thatvisits every vertex of G exactly once (where the cost of a cycle isthe sum of its edge weights).

Solutions to Selected Exercises in Exercises 10.8

1. A 4-color solution is shown in the following diagram:

2. The maximum flow on the graph of Figure 10.31 is 23.

Page 155: Standish Sol Manual Ece223

Table of Contents — continued

Answers to Review Questions 11.21. A Table ADT consists of a table, which is an abstract storage

device containing entries of the form (K, I), where K is a key and Iis some information associated with K, together with someabstract table operations for inserting, deleting, retrieving, andupdating table entries, for enumerat ing the entries in a table inincreasing order of their keys, for init ial izing a table to be theempty table, and for determining if a table is full.

2. The operations we can perform on table T include: initializing T tobe the empty table, determining whether or not T is fu l l ,enumerat ing the table entries in T in increasing order of theirkeys, and ret r iev ing, upda t ing , inser t ing, and de le t ing tableentries in T.

3. Lists of structs having the members Key K and Info I; arrays ofsuch structs; AVL-trees or B-trees containing such structs at thetree nodes; a hash table containing such structs; or parallelarrays with keys K in one array and associated information I in aparallel array, are all feasible representations of a Table ADT.

Solutions to Selected Exercises in Exercises 11.21. Assuming table T is represented by an array of table entry

structs stored in ascending order of their keys: (1) Table T is fullif the array index of the last table entry is one less than themaximum number of table entries that can be stored in the array;(2) To insert a new entry (K, I) in Table T, you can use binarysearching to locate where to insert a new struct of the form {KeyK, Info I}, following which you move all table entries with keysgreater than K up one space and then insert the struct for (K, I)into the hole so created; (3) To delete a table entry (K, I), you canlocate the table entry to delete using binary searching and thenyou can move all structs with keys greater than K down one spacein the array, overwriting the space for the struct that used tocontain (K, I); (4) To retrieve or update the record containing keyK, you can use binary searching to locate the record with key Kand then either retrieve the associated information or store thenew update information; and (5) to enumerate the table entries, asequential scan in the order of increasing array indices can beused.

2. In the case of an AVL-tree table representation, a new member isadded to the struct representing each node of the AVL tree tocontain the information associated with the key at the node.Binary tree searching is used to locate nodes containing a key K

Page 156: Standish Sol Manual Ece223

Table of Contents — continued

for purposes of retrieval and updat ing. The AVL-tree insertionand deletion algorithms given in the solution to Exs. 9.8.3 and9.8.4 can be used to insert and delete new table entries. The tablecan be enumerated in increasing order of the keys by using anInOrder tree traversal of the AVL tree. A table is full when thereis no more memory to allocate to form new AVL-tree nodes.

Answers to Review Questions 11.31. In linear probing, after a first probe is made at location h (K ) ,

subsequent probes are tried at locations h(K) – 1, h(K) – 2, ... , h(K)– i, ... , 2, 1, 0. After the probes fall off the bottom of the table, theprobe sequence “wraps around” to the top of the table and,starting at the topmost table address, moves downwardsequentially in steps of one. In double hashing, after a first probeis made at location h(K), subsequent probes are tried at [h(K) –i*p(K )] mod M, where i is the number of the ith probe, M is thetable size, and p(K) is a second hash function of the key K suchthat p(K) ≥ 1.

2. In collision resolution by separate chaining all keys colliding at agiven hash address h(K ) are placed on a linked list whose firstnode is referenced by a pointer stored in location h (K ) in thetable. This linked list is searched to find a node containing key K.

3. In hashing with buckets a large hash table is divided into anumber of smaller subtables each of which is called a bucket. Thehash function h(K) sends a key K onto a bucket number b. Key K isthen stored sequentially in sorted order of keys inside thebucket (or subtable) with bucket number h(K) = b.

Solutions to Selected Exercises in Exercises 11.31. The table resulting from the order of insertion X24, W23, J10, B2,

N14 and S19 using linear probing is as follows:

0123456 N14

S19

B2

J10

W23

X24

2. The table resulting from the order of insertion X24, W23, J10, B2,N14 and S19 using double hashing is as follows:

Page 157: Standish Sol Manual Ece223

Table of Contents — continued

0123456

N14

S19

B2

J10

W23

X24

3. A complete C program that solves Ex. 11.3.3 is given as follows:

| /*| *| * Ex. 11.3.3 -- Hash insertion and searching using chaining| *

5 | */|| #include <stdio.h>| #include <stdlib.h>|

10 | #define MaxIndex 6| #define TableSize 7||| typedef char KeyType;

15 || typedef struct NodeTag{| KeyType Key;| struct NodeTag *Link;| }Node;

20 || typedef Node * HashTable[TableSize];||| /* -------------------------------------------------------------- */

25 || int h(KeyType K)| {| return (1+ (int)(K) – (int)('A')) % TableSize;| }

30 || /* -------------------------------------------------------------- */|| void InitializeTable(HashTable T)| {

35 | int i;|| for (i=0; i<TableSize; ++i) T[i] = NULL;| }|

40 | /* -------------------------------------------------------------- */|| void Insert(KeyType K, HashTable T)| {| Node *L, *N;

45 || L = T[h(K)]; /* obtain pointer to list on which to insert K */| N = (Node *)malloc(sizeof(Node)); /* create a new list node, N */

Page 158: Standish Sol Manual Ece223

Table of Contents — continued

| N–>Key = K; /* let its Key be K */| N–>Link = L; /* let it link to the remainder of the list */

50 | T[h(K)] = N; /* update T[h(K)] to point to new list */| }|| /* -------------------------------------------------------------- */|

55 | Node *Find(KeyType K, HashTable T)| {| Node *L;|| L = T[h(K)]; /* let L point to the list potentially containing K */

60 || while (L != NULL) {| if (L–>Key == K) {| return L;| }

65 | L = L–>Link;| }|| return L; /* Find returns NULL if K was not on list */|

70 | }|| /* -------------------------------------------------------------- */|| void PrintTable(HashTable T)

75 | {| int i; Node *L;|| for (i=0; i<TableSize; ++i) {| L = T[i];

80 | printf("T[%2d] = (",i);| while (L != NULL) {| putchar(L–>Key);| if (L–>Link != NULL) putchar(',');| L= L–>Link;

85 | }| printf(")\n");| }| putchar('\n');| }

90 || /* -------------------------------------------------------------- */|| void SearchAndPrint(KeyType K, HashTable T)| {

95 | Node *N;|| printf("Searching for Key '%c' in Table T\n",K);| N = Find(K,T);|

100 | if (N != NULL) {| printf(" Found %c\n",N–>Key);| } else {| printf(" Key '%c' not Found in Table T\n",K);| }

105 | }|| /* -------------------------------------------------------------- */|| int main(void)

Page 159: Standish Sol Manual Ece223

Table of Contents — continued

110 | {| HashTable T;|| InitializeTable(T);|

115 | Insert('X',T);| Insert('W',T);| Insert('J',T);| Insert('B',T);| Insert('N',T);

120 | Insert('S',T);|| PrintTable(T); /* prints the Table in Fig. 11.11 on p. 460 */|| printf("Table printed above\n\n");

125 || SearchAndPrint('W',T);| SearchAndPrint('N',T);| SearchAndPrint('A',T);|

130 | }| /* end main program */|| /* the printout resulting from running the program above is:|

135 | T[ 0] = (N)| T[ 1] = ( )| T[ 2] = (B,W)| T[ 3] = (J,X)| T[ 4] = ( )

140 | T[ 5] = (S)| T[ 6] = ( )|| Table printed above|

145 | Searching for Key 'W' in Table T| Found W| Searching for Key 'N' in Table T| Found N| Searching for Key 'A' in Table T

150 | Key 'A' not Found in Table T| */

4. You could redesign the insertion algorithm to put keys on theirchains in alphabetical order (or in key order, whatever it happensto be). Then, when searching for a key K1, if you scan a chain andarrive at a node containing a key K2 which occurs after key K1 inalphabetical order (or key order), you know K1 is not on the chain— hence the search is unsuccessful. The time for unsuccessfulsearching is reduced because, on the average, you need searchonly half of the keys on a chain to determine that the search keyis not on the chain.

Answers to Review Questions 11.4

Page 160: Standish Sol Manual Ece223

Table of Contents — continued

1. Two keys K1 and K2 (where K1 ≠ K2,) are said to collide in a hashtable T if they have identical hash addresses h(K1) = h(K2).

2. A collision resolution policy is a method for finding an emptytable entry in which to store a key K 2 if, after trying to store K 2

in table T, we find that the table location given by the hashaddress h(K2) is already occupied by another previously insertedkey K1.

3. If a hash table T of 365 entries has 23 or more occupied entries,then there is a greater than 50% chance that a new key K willcollide at address h(K) with a previously inserted key, assumingthe hash function h is uniform and random. It might seemcounterintuitive that this could happen in a table that is only 6.3%full, but such is the case.

4. The load factor α of a hash table T is the ratio of the number ofoccupied table entries N to the total number of table entries M.Thus, α = N/M. Generally speaking, the higher the load factor of ahash table, the worse the performance of a hashing methodbecomes.

5. Primary clustering occurs in hashing by open addressing withlinear probing when contiguous clusters of keys form in the hashtable. Each such cluster tends to grow at its low address end at arate proportional to its cluster size, and adjacent clusters tendto join to form even larger clusters which grow even faster(because they form larger targets for collision with new keysbeing inserted).

Solutions to Selected Exercises in Exercises 11.41. A program to compute P(n) is given below. The value of n for

which P(n) ≥ 0.5 and P(n – 1) < 0.5 is n = 23, as can be seen from theprintout that the program produces.

| /*| * Ex. 11.4.1 -- Computing von Mises probabilities| */|

5 | #include <stdio.h>| #include <stdlib.h>||| double P(int n)

10 | {| int i;| double temp;|| temp = 1.0;

15 | for (i=2; i<=n; ++i) {| temp = temp * (365.0 – i + 1) / 365.0;| }

Page 161: Standish Sol Manual Ece223

Table of Contents — continued

|| return 1.0 – temp;

20 | }||| int main(void)| {

25 || int i,TopValue;|| printf("Give top value for n = ");| scanf("%d", &TopValue);

30 || printf("\n n: P(n)\n");|| for (i=1; i<=TopValue; ++i) {| if ( ((i % 5) == 0) || (i == 22) || (i == 23)) {

35 | printf("%4d:%11.8f\n", i, P(i));| }| }| }|

40 | /* The printout produced by this program for the input 100 is:|| n: P(n)| 5: 0.02713557| 10: 0.11694818

45 | 15: 0.25290132| 20: 0.41143838| 22: 0.47569531| 23: 0.50729723| 25: 0.56869970

50 | 30: 0.70631624| 35: 0.81438324| 40: 0.89123181| 45: 0.94097590| 50: 0.97037358

55 | 55: 0.98626229| 60: 0.99412266| 65: 0.99768311| 70: 0.99915958| 75: 0.99971988

60 | 80: 0.99991433| 85: 0.99997600| 90: 0.99999385| 95: 0.99999856| 100: 0.99999969

65 || */

2. If the Barber of Seville shaves himself, then he is one of thosewhom the Barber of Seville does not shave (according to hisshaving policy). Hence he does not shave himself. On the otherhand, if he does not shave himself, then he is one of those whomhe does shave (by his shaving policy). So if he does he doesn’tand if he doesn’t he does — a contradiction in logic.

Answers to Review Questions 11.5

Page 162: Standish Sol Manual Ece223

Table of Contents — continued

1. Given a key K you first compute i = h(K), the hash address of K,and a probe decrement pd = p(K). (In linear probing p(K) ≡ 1 for allkeys, but in double hashing, for two distinct keys K1 and K2, thevalues of p (K 1) and p (K 2) will usually differ, even if K 1 and K 2

collide at the same hash address h(K1) = h(K2).) Next you searchfor an empty table entry, starting first at location i in the table T,and then at locations i – pd, i – 2*pd, and so on. Thus, the sequenceof table locations examined is T[i], T[i – pd], T[i – 2*pd], etc. If, atany time, you fall off the bottom of the table, you wrap around tothe top of the table and start searching downwards again. Thus,the locations examined in the probe sequence are of the form (i –k*pd) % M for k = 0, 1, 2, ... , M–1, where M is the table size. (A fulltable is defined to be a table with exactly one empty entry, soyou are always guaranteed to find an empty entry in every probesequence of a table that is not full, provided you know that theprobe sequence enumerates all possible table addresses.) Whenyou locate the first empty table entry in the probe sequence, youcan insert the key K into that entry.

2. When searching a table T for a key K which is not in T using openaddressing with linear probing, Program 11.17 starts searching atlocation h(K), and continues to probe locations in the sequence(h(K) – i) % M, for i = 0, 1, 2, ... , M – 1. This implies that the probesequence starts at location h(K) and moves linearly downwardtoward the bottom of table T. If search falls off the bottom, itwraps around and starts downward linearly from the top of thetable. The search terminates when the first empty entry in tableT is found (and it is guaranteed that such an empty entry exists,because even a full table has one empty entry by definition).Thus, Program 11.17 will terminate by returning –1 on line 25after having found the EmptyKey at some probe location in theprobe sequence while searching for the key K which was not in thetable.

3. A probe sequence for a key K in a hash table T with tableaddresses in the range 0:M–1, is a permutation (or orderedrearrangement) of the addresses in 0:M–1 starting at the hashaddress h(K). In general, for open addressing, the probe sequenceis of the form (h(K) – i*p(K)) % M for i = 0, 1, 2, ... , M–1, wherep(K) ≡ 1 for linear probing and 1 ≤ p(K) < M for double hashing. Indouble hashing, for two distinct keys K 1 ≠ K 2, we usually havep(K1) ≠ p(K2). Thus, in linear probing, two distinct keys K1 and K2

colliding at h (K 1) = h(K 2) trace out identical probe sequences,starting at h(K1) = h(K2) and moving downward in steps of 1 in T,whereas, in double hashing, two distinct colliding keys K1 and K2

will trace out different probe sequences whenever p(K1) ≠ p(K2).

Page 163: Standish Sol Manual Ece223

Table of Contents — continued

4. The probe sequence is (h(K) – i*p(K)) % M for i = 0, 1, 2, ..., M–1.This probe sequence is guaranteed to cover all addresses in therange 0:M–1 in table T whenever p(K) and M are relatively prime.This will happen whenever: (1) M is a prime number and p(K) is aninteger in the range 1 ≤ p(K) < M, or (2) M is a power of 2 and p(K)is an odd integer in the range 1 ≤ p(K) < M.

5. With α = 0.9, successful search theoretically takes the followingnumber of probes:

(a) for separate chaining CN ≅ 1 + (1/2) α = 1.45

(b) for open addressing with linear probingCN ≅ 12 

1   +  1

1  –   α= 5.50

(c) for open addressing with double hashingCN ≅ 1α  l n

1

1  –  α= 2.56

Hence, theoretically, separate chaining takes the least number ofprobes.

Solutions to Selected Exercises in Exercises 11.51. The solution is not given.

2. The solution is not given.

3. Let us first prove that the triangle numbers mod M, starting at 0,cover the addresses 0:M–1 in a table T of size M, whenever M is ofthe form M = 2n.

Proof:

Suppose there exist i, j ∈ 1:2n for which i ≠ j and

i( i  –   1 )2 mod 2n =

j(j – 1)2 mod 2n.

Then, i(i – 1)

2 – j(j – 1)

2 must be evenly divisible by 2n.

Buti( i  –   1 )

2 – j(j – 1)

2

=i2   –   i   –   j 2   +   j

2 = ( i2  –   j2 )  –   ( i  –   j)

2

=( i   +   j ) ( i   –   j )   –   ( i   –   j )

2

=( i   +   j   –   1 )   ( i   –   j )

2 .

Thus, ( i   +   j   –   1 )   ( i   –   j)

2 must be evenly divisible by 2n .

Therefore, (i +   j –  1 ) ( i – j) must be evenly divisible by2n+1. Now, we consider two cases:

Page 164: Standish Sol Manual Ece223

Table of Contents — continued

Case 1: i and j are both even or both odd. Then (i + j – 1) is odd,so 2n+1 must divide evenly into (i – j). But with i, j ∈ 1:2n,all of the differences of the form i – j are less than 2n,and hence none are divisible by 2n+1.

Case 2: i and j are of opposite parity. I.e., i is even and j is odd,or i is odd and j is even. Then (i – j) is odd, so 2n+1 mustdivide evenly into (i + j – 1). But the largest that i + j – 1can be with i, j ∈ 1:2n  and i ≠ j is 2n+1 – 2, which is againnot divisible by 2n+1.

Consequently, the numbers i(i – 1)

2 mod 2n are distinct for all

i ∈ 1:2n.

No te : It is important to choose i ∈ 1:2n , whence the trianglenumbers start at 0. If you start the triangle numbers at 1, with i =

2 in i( i –   1 )

2 = 2 (2  – 1 )

2 = 1, then you get a duplicate for i = 2n

and j = 2n + 1, where i ≠ j and i, j ∈ 2:2n +1.

In the proof above when i and j are of opposite parity (Case 2),2n+1 must divide (i + j – 1) evenly. But if i = 2n and j = 2n + 1, (i + j –1) = (2n +2n + 1 – 1) = 2n+1, which is evenly divisible by 2n+1.

For example: Let n = 3, so 2n = 8, and look at the results in thefollowing table:

ii ( i  –   1 )

2i( i  –   1 )

2 mod 8

1 0 02 1 13 3 34 6 65 10 26 15 77 21 5

8 28 4 Note the duplicates in the third9 36 4 column for i = 8 and i = 9.

10 45 5

As i increases, the remainders mod 8 for i ∈ 9:16 are the reverseof the sequence of remainders for i in 1:8.

The following is a complete C program that illustrates trianglenumber hashing.

Page 165: Standish Sol Manual Ece223

Table of Contents — continued

| /*| * Ex. 11.5.3 -- Triangle Number Hashing, pp. 484-486| */|

5 | #include <stdio.h>|| #define TABLESIZE 8| #define EmptyKey '\0'|

10 | typedef char KeyType;|| typedef int InfoType;|| typedef struct {

15 | KeyType Key;| InfoType Info;| } TableEntry;|| typedef TableEntry Table[TABLESIZE];

20 ||| Table T; /* declare T to be a Table of TableEntries */| /* indexed by 0:TABLESIZE–1 */|

25 | /* ------------------------------------------------------ */|| int h(KeyType L)| {| return (1 + (int)(L) – (int)('A')) % TABLESIZE;

30 | }|| /* ------------------------------------------------------ */|| void InitializeTable(Table T)

35 | {| int i;|| for (i=0; i<TABLESIZE; ++i) {| T[i].Key = EmptyKey;

40 | T[i].Info = 0;| }| }|| /* ------------------------------------------------------ */

45 || void PrintTable(Table T)| {| int i;|

50 | for (i=0; i<TABLESIZE; ++i) {| if (T[i].Key != EmptyKey) {| printf("%2d: %c–%d\n",i,T[i].Key,1 + (int)(T[i].Key) – (int)('A'));| } else {| printf("%2d: %c\n",i,T[i].Key);

55 | }| }| putchar('\n');| }|

60 | /* ------------------------------------------------------ */|| void HashInsert (KeyType K, InfoType I)

Page 166: Standish Sol Manual Ece223

Table of Contents — continued

| {| int i, j;

65 | KeyType ProbeKey;|| /* Initializations */| i = h(K); /* first hash */| j = 0; /* initialize counter for triangle number hashing */

70 | ProbeKey = T[i].Key;|| /* find first empty slot */| while (ProbeKey != EmptyKey) {| ++j; /* increment triangle number hashing counter */

75 | i –= j; /* compute next probe location */| if (i < 0) {| i += TABLESIZE; /* wrap around if needed */| }| ProbeKey = T[i].Key;

80 | }|| /* Insert new key and info into table */| T[i].Key = K;| T[i].Info = I;

85 | }|| /* ------------------------------------------------------ */|| int HashSearch (KeyType K)

90 | {| int i,j;| KeyType ProbeKey;|| /*Initializations */

95 | i = h(K); /* first hash */| j = 0; /* intialize counter for triangle number hashing */| ProbeKey = T[i].Key;|| /* Find either an entry with key K, or the first empty entry */

100 | while (ProbeKey != K && ProbeKey != EmptyKey) {| ++j; /* increment triangle number hashing counter */| i –= j; /* decrement probe location by amount j */| if (i < 0) { /* wrap around if needed */| i += TABLESIZE;

105 | }| ProbeKey = T[i].Key;| }|| /* return the position of key K in table, or return -1 if K not in T */

110 | if (ProbeKey == EmptyKey) {| return –1; /* return –1 to signify K was not found */| } else {| return i; /* return location, i, of key K in table T */| }

115 | }|| /* ------------------------------------------------------ */|| void SearchAndReportLocation(KeyType K)

120 | {| int result;|| result = HashSearch(K);| printf("Searched for Key %c, ",K);

Page 167: Standish Sol Manual Ece223

Table of Contents — continued

125 | printf("Found it at Table Location == %d\n",HashSearch(K));| }|| /* ------------------------------------------------------ */|

130 | int main(void)| {| InitializeTable(T);|| HashInsert('B',0);

135 | HashInsert('J',0);| HashInsert('S',0);| HashInsert('N',0);| HashInsert('X',0);| HashInsert('W',0);

140 | HashInsert('A',0);| PrintTable(T);|| SearchAndReportLocation('N');| SearchAndReportLocation('B');

145 | SearchAndReportLocation('J');| SearchAndReportLocation('X');| SearchAndReportLocation('S');| SearchAndReportLocation('W');| SearchAndReportLocation('A');

150 | SearchAndReportLocation('C');| }| /* end of main */|| /* The printout obtained from running the program above is:

155 || 0: X-24| 1: J-10| 2: B-2| 3: S-19

160 | 4: A-1| 5:| 6: N-14| 7: W-23|

165 | Searched for Key N, Found it at Table Location == 6| Searched for Key B, Found it at Table Location == 2| Searched for Key J, Found it at Table Location == 1| Searched for Key X, Found it at Table Location == 0| Searched for Key S, Found it at Table Location == 3

170 | Searched for Key W, Found it at Table Location == 7| Searched for Key A, Found it at Table Location == 4| Searched for Key C, Found it at Table Location == –1|| */

Answers to Review Questions 11.6 1. A hash function h (K ) performs well if the hash addresses it

computes for keys K are distributed evenly in a hash table’saddress space. A good hash function disperses clusters of keys inthe key space by mapping them into dispersed hash tableaddresses. A hash function performs poorly if it does not

Page 168: Standish Sol Manual Ece223

Table of Contents — continued

distribute hash addresses evenly in a hash table or if it fails todisperse clusters of keys.

2. In the division method of hashing, an integer representation of akey K is divided by the hash table size M, yielding a quotient Qand a remainder R. The remainder R is used as the value of thehash function h(K), and the quotient Q, reduced modulo the tablesize M, and replaced by 1 if (Q mod M) was zero, is used as theprobe decrement p(K). In symbols, p(K) = max(1, Q mod M). In thiscase, the probe decrement p(K) is a “second hash function,” whichusually yields different values of p (K ) for different keys. The“first hash function” h(K ) = (K % M), determines the first probeaddress. The term “double hashing” refers to the fact that twoseparate hash functions are used to determine the remainder ofthe probe sequence after the first probe.

3. One example of a pitfall when choosing a hash function is to usethe division method and to use a table size which is a power of 2,such as M = 21 2 = 4096. In this case h (K ) selects the leastsignificant 12 bits of the binary representation of keys K, whichpreserves clusters of keys and tends to mirror the unevendistribution patterns of keys in the set of keys to be hashed.

Solutions to Selected Exercises in Exercises 11.61. Witt chose a poor hash function which failed to avoid the pitfall

explained in the answer to Review Question 11.6.3 above. Forexample, Witt’s hash function will take clusters of keys, such asX1, X2, and X3 or P1, P2, and P3 in the key space, and will mapthem onto contiguous clusters of hash addresses in the hash tableaddress space.

2. A short program illustrating multiplicative hashing is:

| /*| * Ex. 11.6.2 -- Multiplicative hashing| */|

5 | #include <stdio.h>| #include <stdlib.h>|| int main(void)| {

10 | long A, Result, W, i;|| A = 20071;| W = 32768;|

15 | for (i=0; i<16; ++i) {| Result = ((A*(i+1)) % W) / 2048;| printf("%3ld",i);printf(": %3ld\n",Result);| }|

Page 169: Standish Sol Manual Ece223

Table of Contents — continued

20 | }| /* end main */|| /* The printout for this program is:|

25 | 0: 9 8: 8| 1: 3 9: 2| 2: 13 10: 11| 3: 7 11: 5| 4: 1 12: 15

30 | 5: 10 13: 9| 6: 4 14: 3| 7: 14 15: 12|| */

3. The solution is not given.

Answers to Review Questions 11.71. A Table ADT could be represented by: (a) a sorted array of

structs, (b) an AVL-tree of node structs, or (c) a hash table.

2. In the case of a hash table representation of a Table ADT,insertion, retrieval, and update follow the normal procedures forhash table insertion and search, with the understanding that aftersuccessful search, the update operation modifies the informationmembers of a retrieved struct for a table entry. Enumerationrequires sorting the hash table entries before it is possible toenumerate them in the order of their keys. In some hash tablerepresentations, such as those for open addressing, deletion canbe accomplished by marking table entry structs as deleted eventhough they are still physically present in the hash table. Such apolicy is necessitated by the fact that actual physical removal ofdeleted table entry structs leaves empty entries which mayinterrupt and abort successful search for other keys present inthe table (because, during search or insertion, when an emptyentry is encountered during the probe sequence, it signals eitherunsuccessful search or the successful location of an address inwhich to insert a new table entry).

Solutions to Selected Exercises in Exercises 11.71. A hash table representation of a Table ADT will be superior under

circumstances when deletions and table enumerations are rare,and when the table is not only not fully saturated, but also rarelyoverflows, necessitating rehashing into a table of larger size.

2. An AVL-tree representation of a Table ADT could be superiorunder circumstances in which deletions, insertions, andenumerations are frequent and numerous.

Page 170: Standish Sol Manual Ece223

Table of Contents — continued

3. The solution is not given.

Page 171: Standish Sol Manual Ece223

Table of Contents — continued

Answers to Review Questions 12.21. The latency time is the rotational delay until the proper sector

moves beneath the read/write head.

2. The seek time is the time to move the read/write heads to theproper track (or cylinder).

3. The transmission time is the rotational delay while the portion ofthe track containing the record to be read or written passes underthe read/write head.

Solutions to Selected Exercises in Exercises 12.21. The average latency time is the time for one half a rotation. If

there are 60 seconds in a minute and the disk rotates 3600 timesin one minute, then the seconds per rotation are 60/3600, and theseconds for one half a rotation are:

6 02*3600 =

12 * 6 0 =

1120 = 0.008333, or 81 /3

milliseconds per rotation.

2. Prove that the average of |i – j| for i, j chosen randomly ∈ 1:n is

A =   2    

n+1

3 n 2 , where

n+1

3 = (n+1)(n)(n–1)

6 .

We need to compute A =

∑i= 1

n      ∑

j= 1

n| i  –   j|

 n 2 .

Consider an n × n matrix T with rows i and columns j such that T[i, j]= |i – j|. The matrix is symmetric and has a zero diagonal, since T[i,j] = |i – j| = |j – i|= T[j, i] and T[i, i] = |i – i| = 0. So if S is the sum ofthe elements above the diagonal, the average is given by A =2*S/n2. The n elements of T on the diagonal are zero. The (n–1)elements above the diagonal are 1. The (n–2) elements two abovethe diagonal are 2. And so on, until the one element in the upperright corner is (n–1). Hence, the sum S of the elements of T above

the diagonal is, S = ∑i= 0

n    i* ( n –   i) .

For example, for n = 5, the portion of the matrix T on and abovethe diagonal is:

1 2 3 4 5

Page 172: Standish Sol Manual Ece223

Table of Contents — continued

1 0 1 2 3 4

2 0 1 2 3

3 0 1 2 Here, we have S = 0*5 + 1*4 + 2*3 + 3*2 +4*1.

4 0 1

5 0

Hence, S = ∑i= 0

n   i* (n –   i) = ∑

i= 0

n i*n – ∑

i= 0

n    i 2 .

Using Formula A.29 from the “Math Reference” appendix for thesum of the squares ∑(1≤i≤n) i2 = n(n+1)(2n+1)/6, we get,

S = n 

∑i=0

n  i –

n(n+1)(2n+1)6

= n

n(n+1)

2 – n(n+1)(2n+1)

6 = n(n+1)

6 [3n – 2n – 1]

= (n+1)(n)(n–1)

6 =

n+1

3 .

But A = 2 S/n2, so

A =  2    

n+1

3 n 2 , which is what we needed.

3. We can use the result of the previous exercise to prove that theaverage of |i – j| for i, j chosen randomly from 1:n is ≈ n/3.

A =  2    

n+1

3 n 2 =

2(n+1)(n)(n–1)6n2 .

Simplifying A, we get

A = (n2 – 1)n

3n2 = n3

3n2 – n

3n2 = n3 –

13n .

Hence, the difference between n/3 and A is n /3 – A = 1/3n. Thequantity 1/3n. becomes small as n becomes large. For example, forn = 100,

A =   2    

101

3 1 0 0 2 =

2 * 1 6 6 6 5 010000 = 33.33, and

n3 =

1 0 03 =

33.333333... .

Page 173: Standish Sol Manual Ece223

Table of Contents — continued

The difference between A and n/3, is 1/300 = 0.0033333... , which isabout 1 /1 0 0 th of a percent of A’s value. In fact, for n ≥ 11, n /3

differs from A by less than one percent of A’s value.

Answers to Review Questions 12.31. Four such techniques are: binary searching in a sorted contiguous

array, searching in an AVL tree representation of a Table ADT,sorting an unsorted contiguous array by comparison/exchangesorting, and searching for a record containing key K in a hash tableusing open addressing and double hashing.

2. Even though AVL trees are well-balanced, on the average, thebranching factors at their internal nodes are at most two, whichimplies that AVL trees containing large numbers of nodes willrequire many nodes to be searched in an average case, eventhough the number of nodes searched is O(log n). For example, inan AVL tree with 106 nodes, it takes about 21.4 nodes accesses inthe average case and 29 node accesses in the worst case to find anode containing a search key K. At one disk access per node, theAVL-tree representation is too inefficient for practical use.

Solutions to Selected Exercises in Exercises 12.31. On the average, in an AVL tree of n nodes, it takes lg n + 1.5 disk

accesses at one disk access per node to find a node containing asearch key K . Hence, for n = 16.777 million nodes, it takesapproximately 25.5 disk accesses for an average successfulsearch.

2. Regardless of the table size M, when the load factor of the table isα , it takes CN = (1 /α ) ln(1 /1– α ) hash probes to find a key K in ahash table using open addressing and double hashing. Thus for α =0.95, CN ≅ 3.15 disk accesses at one disk access per hash probe.

Answers to Review Questions 12.41. The B-tree of order 200 works well and the 2-3 tree works poorly

because, for a given number of records n, it takes a tree of manyfewer levels to store n records when the node branching factor ishigh than when the node branching factor is low, and the fewer thenumber of tree levels, the fewer the number of disk accessesrequired during searching. In particular, in a B-tree of order m thatindexes n keys, the worst case number of nodes l that we have tosearch is ≤ log m /2 ((n +1)/2). As a result, if a B-tree of orderapproximately m = 200, is used to represent a Table ADTcontaining no more than n = 1,999,998 keys, then the number of

Page 174: Standish Sol Manual Ece223

Table of Contents — continued

disk accesses is 3 or less. This is substantially better than AVL-trees which need 22.4 accesses on the average to access a key inan AVL-tree containing 1,999,998 keys.

2. Merge sorting works better than selection sorting becauseselection sorting requires a large number of accesses to recordson disk to implement comparisons and exchanges, whereas mergesorting reads and writes contiguous blocks of records efficientlyfrom/to disk and rearranges such blocks into sorted orderefficiently in internal (primary) memory.

3. Hashing using buckets of size 50 works better than double hashingwith open addressing, because the latter technique traces outprobe sequences that hop all over the disk at one disk access perprobe, whereas the former almost always leads to a bucketcontaining the search key in one disk access, following which theentire bucket is read into primary memory and is subsequentlysearched efficiently using binary searching.

Solutions to Selected Exercises in Exercises 12.41. For n = 4 million, CN ≈ lg n + 1.5 node accesses on the average in an

AVL-tree, so CN = 23.43 disk accesses at one disk access per node.

2. If we use 7 of the 10 tracks per cylinder as prime tracks and 120of the 128 cylinders as prime cylinders, we can accommodate:

120 cyl inders

d isk *

7   t r acks

cyl inder *

64 sec to rs

t rack *

2048 by tes

sector =

110,100,480 bytes

in the prime storage area for records on a single disk, and, at 16bytes/record, we can store 6,881,280 records. Hence, our 4 millionrecord ISAM file will fit onto one disk of the kind whosecharacteristics are given in Table 12.5 (on page 503). In such anISAM file, assuming a master index is stored in primary memory,we can access a record using at most 3 disk accesses if nooverflows occur.

Answers to Review Questions 12.51. A fully inverted file F is one for which each value v of each

attribute a in each record R of F is in an index such that v can belooked up in the index and will lead to a list of all records havingthe value v for some attribute.

2. A query language is a language in which information retrievalqueries can be expressed, such as queries: to find a recordcontaining a key K; to find all records whose A-attribute either hasthe value v or lies in some range R 1 ≤ v ≤ R 2; to find the sum,

Page 175: Standish Sol Manual Ece223

Table of Contents — continued

maximum, minimum, or average of the A-attribute of all recordssatisfying some search criterion; to find Boolean combinations ofsimpler queries having truth values (true, false) as results; and soforth.

3. A database is a collection of files concerned with a particularorganization or enterprise. A database system is a databasetogether with the set of machines, people, policies, andprocedures that govern its use in the context of the enterprise.

Solutions to Selected Exercises in Exercises 12.51. (and 2.) The answers are dependent on the details of the reader’s

institution and are not given.

Page 176: Standish Sol Manual Ece223

Table of Contents — continued

Answers to Review Questions 13.21. A comparison-based sorting method is one that compares values

of pairs of keys and rearranges their order depending on theoutcomes of the comparisons.

2. A comparison tree is a binary tree in which each internal noderepresents a comparison between a specific pair of keys and inwhich the internal node’s two descendants represent the “yes” and“no” outcomes of the comparison. A leaf in a comparison treerepresents a particular sorted ordering of the keys implied by theoutcomes of the comparisons along the path from the root node tothe leaf.

3. n log n.

4. The full binary trees having minimum external path length arethose with leaves on at most two adjacent levels..

5. The average number of comparisons needed to sort n keys usingthe comparisons in a comparison tree T is the external path lengthof T divided by the number of external nodes in T (where thenumber of external nodes is the number of different sortedarrangements of the n keys, and the external path length is thesum of the path lengths from the root to each external node).Hence, this average is at a minimum when the external path lengthof T is at a minimum.

Solutions to Selected Exercises in Exercises 13.21. In a full binary tree with k leaves on at most two adjacent levels,

let r be the smallest level number of a level containing at leastone leaf. If all k leaves are on level r, there are exactly k = 2r

leaves and r = lg k = lg k , so a path from the root to each leafcontains exactly r edges. On the other hand, suppose at least oneleaf is on level r and some other leaves are on level r + 1. In thiscase, level r + 1 can contain up to 2r+1 – 2 leaves, while at least oneleaf remains on level r. The maximum number of leaves that canoccur on two adjacent levels with at least one leaf on level r istherefore k = 2r+ 1 – 1. Here, we again have r = lg k , becausek = 2r+1 – 1 ⇒ (implies) 2r ≤ k < 2r+1 ⇒ r ≤ lg k < r + 1 ⇒ lg k = r.Again, a path from the root to any leaf in such a tree must containat least r edges (because all such paths from the root to variousleaves contain either r or r + 1 edges).

2. Applying formula A.22 of the “Math Reference appendix” to changelogarithm bases, we have lg (n!) = ln(n!)/ ln 2. Hence, lg(n!) ≈ ln(n!)/

Page 177: Standish Sol Manual Ece223

Table of Contents — continued

ln 2 + O(n), which still holds if we replace the exact equivalencewith approximations such as Stirling’s approximation.

3. O(n log n).

Answers to Review Questions 13.31. In abstract priority queue sorting, the keys to be sorted are

inserted into a priority queue PQ in any order. Then they areremoved, one-by-one, in order of highest to lowest priority andare inserted in arrival order on the rear end of an initially emptyqueue Q. The order of arrival in Q is thus the sorted order ofhighest-to-lowest priority of the keys.

2. In Select ionSort using an array A, the array is partitioned into anunsorted subarray A[0: i], representing a priority queue, and asorted subarray A[i+1:n–1] representing an output queue. To findthe highest priority (largest) key in the priority queue, you scanA [0:i] to find the position j of the largest key and remove A[j] asthe highest priori ty key. Hence, the “unsorted arrayrepresentation” of a priority queue is used in SelectionSort.

3. In HeapSor t , the priority queue is represented by A[1:i] which isinitially rearranged into a heap whose highest priority elementresides in A[1]. The subarray A[i+1:n] represents an output queue.(An array of size n+1 is used with A[0 ] unoccupied and A[1:n ]occupied by keys to sort.)

Solutions to Selected Exercises in Exercises 13.31. Demonstrate that S = ∑(2≤i≤n) ( lg i +2) is O(n log n). Using formula

A.31 (which is ∑1≤i≤n

       l g   i  = (n + 1 ) q – 2(q + 1 )+ 2, where q =

lg(n+1) ), we have

S = ∑2≤i≤n

     (   l g   i     +   2   ) = ∑

2≤i≤n

       lg   i  + ∑

2≤i≤n

     2

= ∑1≤i≤n

       lg i  + 2(n – 1) = (n+1)q – 2(q+1) + 2n, where q =

lg(n+1)= (n + 1) lg(n+1) – 2(q+1) + 2n

< (n + 1) lg(n+1) – (n+1) + 2n

= n lg(n+1) + lg(n+1) + n – 1.

To deduce the second line above from the line above it, we usedtwo facts: (a) that lg(n+1) ≤ lg(n+1), and (b) that – 2(q+ 1) < –

Page 178: Standish Sol Manual Ece223

Table of Contents — continued

(n +1). The second of these facts follows from the followingargument. For any real number x, we have x < x + 1, from which 2x

< 2( x +1) and – 2( x +1) < – 2x. Now letting x = lg(n+1) the latterbecomes –2( lg(n+1) +1) < – 2lg(n+1), which implies – 2(q+1) < – (n+1).

Now we need to use the fact that lg(n + 1) < lg n + 1 for n > 1, whichis proved on p. 709 of the “Math Reference appendix.” Applyingthis to the expression on the last line above, we get

n lg(n+1) + lg(n+1) + n – 1

< n(lg n + 1) + lg(n+1) + n – 1

= n lg n + lg(n+1) + 2 n – 1

which is O(n log n).

2. SiftUp(A,i,n) is applied to the nodes in the range 2 to n/2 in reverseorder because: (1) n/2 computes n/2 , since in C, if n is an integer,n /2 signifies the quotient of n divided by 2 with the remainderthrown away. Now, by Table 9.7 (on p. 345), node i is a leaf node iff2*i > n, which is the same as saying that the nodes i in the range n/2 < i ≤ n are leaves, from which it follows that the nodes i in therange 1 ≤ i ≤ n/2 are internal nodes. Si f tUp is applied only tointernal nodes since it is useless to apply it to leaves (which aresingle nodes, and hence are heaps already); (2) SiftUp is not appliedto the root node i = 1 when heapifying, because the tree rooted atthe root node gets heapified at the beginning of the second phaseof HeapSort; (3) The internal nodes, except for the root node, areheapified in the reverse of level order to guarantee that the leftand right subtrees of each internal node have already beenheapified. Hence, the order of enumeration is from n/2 downwardsto 2 in steps of 1.

Answers to Review Questions 13.41. In divide-and-conquer methods for sorting, an initially unsorted

array of keys is d i v ided into two or more subarrays that areconquered (sorted separately), after which the sorted subarraysare combined in some fashion to yield the entire sorted array thatsolves the original sorting problem.

2. In the MergeSor t method, an unsorted array is divided into twounsorted arrays of (roughly) equal size which are merge sorted toyield two sorted subarrays. These sorted subarrays are thenmerged by imagining that they are queues, repeatedly removingthe larger key from the front of either of the two queues, andinserting it on the rear of an initially empty output queue.

3. QuickSort is an instance of a divide-and-conquer method because itdiv ides an initially unsorted array A into two adjacent unsorted

Page 179: Standish Sol Manual Ece223

Table of Contents — continued

arrays L and R that contain keys that are respectively less than orequal to some initially chosen pivot key p and greater than orequal to the chosen pivot key p. These unsorted subarrays L and Rare then recursively sorted in place using QuickSort , after whichthere is no need to combine them explicitly because they arealready in sorted order as subarrays of A.

Solutions to Selected Exercises in Exercises 13.41. When the pivot key is always the smallest (or largest) key in the

subarray being partitioned, the partition sizes are as lopsided aspossible, causing QuickSort to run in time O(n2). For example, if thepivot key is chosen as the middle key in A[i:j] with pivot = A[(i+j)/2],an initial unsorted array such as A = [2, 7, 4, 1, 3, 5, 6] will causethe QuickSort algorithm of Program 13.14 to run in worst case time.The sequence of right partitions, in this case, is:

array index i = 0 1 2 3 4 5 6

initial array A = [ 2, 7, 4, 1, 3, 5, 6 ], swap A[0] == 2 & A[3]== 1

R = A[1:6] in

A = [ 1, 7, 4, 2, 3, 5, 6 ], swap A[1] == 7 & A[3] == 2

R = A[2:6] in

A = [ 1, 2, 4, 7, 3, 5, 6 ], swap A[2] == 4 & A[4] == 3

R = A[3:6] in

A = [ 1, 2, 3, 7, 4, 5, 6 ], swap A[3] == 7 & A[4] == 4

R = A[4:6] in

A = [ 1, 2, 3, 4, 7, 5, 6 ], swap A[4] == 7 & A[5] == 5

R = A[5:6] in

A = [ 1, 2, 3, 4, 5, 7, 6 ], swap A[5] == 7 & A[6] == 6

R = A[6:6] in

A = [ 1, 2, 3, 4, 5, 6, 7 ].

A complete C program which uses this example is as follows:

| /*| * Ex. 13.4.1 -- Worst Case QuickSort| */|

5 || #include <stdio.h>| #include <stdlib.h>|| #define MaxIndex 7

10 || typedef int KeyType;|

Page 180: Standish Sol Manual Ece223

Table of Contents — continued

| typedef KeyType SortingArray[MaxIndex];|

15 || /* ----------------------------------------------------- */|| void PrintArray(SortingArray A)| {

20 | int i;|| printf("i = ");| for (i=0; i<MaxIndex; ++i) printf("%2d ",i);| putchar('\n');

25 | printf("A = ");| for (i=0; i<MaxIndex; ++i) printf("%2d ",A[i]);| putchar('\n');| }|

30 | /* ----------------------------------------------------- */|| void Partition (SortingArray A, int *i, int *j)| {| KeyType Pivot, Temp;

35 || Pivot = A[(*i + *j) / 2];|| do {| while (A[*i] < Pivot) (*i)++;

40 | while (A[*j] > Pivot) (*j) – –;| if (*i <= *j) {| Temp = A[*i]; A[*i] = A[*j]; A[*j] = Temp;| (*i)++;| (*j) – –;

45 | }| } while (*i <= *j);| }|| /* ----------------------------------------------------- */

50 || void QuickSort (SortingArray A, int m, int n)| { /* to sort the subarray A[m:n] into ascending order */| int i,j;|

55 | if (m < n) {| i = m;| j = n;| Partition(A, &i, &j);| printf("After partition, j = %2d, i = %2d, and Array A = \n",j,i);

60 | PrintArray(A);| QuickSort(A, m, j);| QuickSort(A, i, n);| }| }

65 || /* ----------------------------------------------------- */|| int main(void)| {

70 | SortingArray A = {2,7,4,1,3,5,6}; /* Initialize A to [2,7,4,1,3,5,6] */|| PrintArray(A);| QuickSort(A, 0, MaxIndex–1);| PrintArray(A);

Page 181: Standish Sol Manual Ece223

Table of Contents — continued

75 || printf("-- end of program\n");|| }| /* end main program */

80 ||| /*| When executed, the program above prints:|

85 | i = 0 1 2 3 4 5 6| A = 2 7 4 1 3 5 6| After partition, j = 0, i = 1, and Array A =| i = 0 1 2 3 4 5 6| A = 1 7 4 2 3 5 6

90 | After partition, j = 1, i = 2, and Array A =| i = 0 1 2 3 4 5 6| A = 1 2 4 7 3 5 6| After partition, j = 2, i = 3, and Array A =| i = 0 1 2 3 4 5 6

95 | A = 1 2 3 7 4 5 6| After partition, j = 3, i = 4, and Array A =| i = 0 1 2 3 4 5 6| A = 1 2 3 4 7 5 6| After partition, j = 4, i = 5, and Array A =

100 | i = 0 1 2 3 4 5 6| A = 1 2 3 4 5 7 6| After partition, j = 5, i = 6, and Array A =| i = 0 1 2 3 4 5 6| A = 1 2 3 4 5 6 7

105 | i = 0 1 2 3 4 5 6| A = 1 2 3 4 5 6 7| -- end of program| */

2. In some versions of QuickSort, the pivot is chosen to be A[0 ], thefirst item in the array. These versions of QuickSort do not work wellwhen the array A is already in sorted order, or the reverse ofsorted order, or nearly so. In such cases, it is advantageous to usethe median of three method. For the version of QuickSort given inProgram 13.14, arrays of the sort illustrated in the solution to Ex.13.4.1 above cause the partitions to be as lopsided as possible.Using the median of three method helps eliminate the lopsidedpartitions in these cases also.

Answers to Review Questions 13.51. We split the array A into two subarrays S and U, where S = A[0:i–1]

is a sorted subarray and U = A[i:n–1] is an unsorted array. For eachi in the range 1:n–1 we choose K = A[i], the leftmost key in U, as akey to insert, and we insert it into S in sorted order, whichenlarges S by one more item and shrinks U by one item. The waywe insert K into S is to move every key in S that is bigger than Kone space to the right, which leaves a hole in which to insert K ,after which we insert K into this hole.

Page 182: Standish Sol Manual Ece223

Table of Contents — continued

2. In TreeSort we insert the keys to sort one-by-one into an initiallyempty binary search tree (or AVL-tree) T after which we readthem off in sorted order using an InOrder traversal of T.

3. InsertionSort runs in time O(n2) and TreeSort runs in time O(n log n).

Solutions to Selected Exercises in Exercises 13.51. Suppose A is the array A = [2, 3, 4, 5, 1] and we make the initial function

call InsertionSort(A,0,4) in which m == 0 and n == 4. This causes a recursivecall on InsertionSort(A,0,3) after which C is set to A[4], which is 1, and i isset to 4, in a situation in which m == 0 and n == 4. Subsequently, theitems in A[0:3] are shifted right into A[1:4], after which m == 0, i == 0, andC == 1. At this moment, the while-condition on line 14 of thefunction is again evaluated. This time A[i–1] tries to access A[–1]

which is outside the range of the array A[0:4], and if array rangechecking has been turned on, an array bounds error will bedetected and reported. To fix the bug, simply replace line 14 bywhile ( (i > m) && (A[i–1] > C) ) {. In this case, the test (i > m) will fail when i

== 0 and m == 0, and the short-circuit and operator && willprevent (A[i–1] > C) from being evaluated. Then the while-loop onlines 14:17 will terminate properly, and C will be placed in the holeA[i] on line 20 in a proper fashion.

2. In InsertionSort, for each i in 1:n–1 half the keys in A[0:i–1] are moved(i.e., i/2 of the i keys in A[0:i–1] are moved), on the average, afterwhich A[i] is moved into the hole opened up for insertion. Summingup the total number of these moves gives the sum S, where

S = ∑i=1

n–1

   

 i2  +   1 =

12   

  ∑i=1

n–1    i + (n – 1)

= 12

   n   (n   –   1 )

2 + (n – 1)

= 14 [ ]   n   (n   –   1 )     +   4   ( n   –   1 )

S =  (n  + 4) (n   –   1 )

4 moves of keys.

In SelectionSort, one pair of keys is exchanged for each i in the range1:n–1, meaning that exactly (n–1) pairwise exchanges of keys takeplace. If each pairwise exchange takes 3 moves, the total numberof key moves is 3n – 3 in SelectionSort. Because 3n – 3 is less than(n+4)(n–1)/4 for all n ≥ 9, Select ionSor t moves fewer keys thanInser t ionSor t on the average whenever the array to be sortedcontains nine or fewer keys.

Page 183: Standish Sol Manual Ece223

Table of Contents — continued

[On the other hand, Insert ionSort performs half as many keycomparisons on the average as Se lec t ionSor t , with Inser t ionSor t

performing n(n–1)/4 comparisons, and SelectionSort performing n(n–1)/2 comparisons.]

Answers to Review Questions 13.61. ProxmapSort scans through an array to be sorted and precomputes

the locations that small subarrays of keys will occupy in finalsorted order before moving any keys. It also computes a mapping,called a proximity map, or proxmap for short, that sends each keyonto the beginning of its reserved subarray in final sorted order.In a final pass, keys are moved to their final reserved subarrayswhere they are insertion-sorted into position.

2. RadixSort makes k linear passes through keys containing k digits.The first pass sorts the keys on the least significant digit, thesecond pass sorts the output of the first pass on the second leastsignificant digit, and the ith pass sorts the output of the (i–1)s t

pass on the ith least significant digit. The process ends when thekeys have been sorted on the most significant digit.

3. RadixSort runs in time O(n) because, for any n keys of k-digits each,it makes k linear passes, thus using time proportional to k *n ,which is O(n).

Solutions to Selected Exercises in Exercises 13.61. See the function RadixSort in the program that follows:

| /*| * Ex. 13.6.1 -- Radix Sorting Example Using 3-Letter Airport Codes| */|

5 | /* This program uses a parallel array representation of linked-list nodes. */| /* The SortingArray, A, contains the keys, which are three-letter Airport */| /* Codes. In parallel to the SortingArray is a Link array holding links to */| /* successor nodes on the linked list of nodes holding AirportCodes and */| /* links. There is also an array of 26 "Bins" -- one for each letter of the */

10 | /* alphabet. Each bin has a Front pointer and a Rear pointer which point to */| /* the respective front and rear of a linked list of nodes that fall into that bin */| /* during Radix Sorting. */|| #include <stdio.h>

15 | #include <stdlib.h>|| #define MaxIndex 32| #define MaxLetter 'Z'+1| #define null 0

20 || typedef int LinkAddress;|

Page 184: Standish Sol Manual Ece223

Table of Contents — continued

| typedef char AirportCode[4];|

25 | typedef AirportCode SortingArray[MaxIndex];|| typedef LinkAddress LinkArray[MaxLetter];||

30 | LinkAddress Link[MaxIndex];|| LinkArray Front,Rear;|| SortingArray A = { "AAA", "DUS", "MEX", "ORD", "RAL",

35 | "ABQ", "ONT", "NRT", "YYZ", "GLA",| "ZRH", "BUF", "DFW", "DEN", "ARN",| "JAX", "BRU", "CRQ", "MIA", "SEA",| "STL", "HOU", "SAN", "GCM", "HKG",| "PIT", "MKC", "SFO", "JFK", "ORY",

40 | "LGA", "ORL"| };|| LinkAddress ListHead;|

45 | /* ------------------------------------------------------------------------------- */|| void Insert(LinkAddress L, int k)| {| LinkAddress N;

50 | int C;|| /* inserts (Airport,Link) pair on rear of list in Bin(A[L][k]) */| C = A[L][k]; /* extract kth-radix digit used to index bins */| if (Front[C] == null) {

55 | Front[C] = L;| Rear[C] = L;| } else {| Link[Rear[C]] = L;| Rear[C] = L;

60 | }| }|| /* ------------------------------------------------------------------------------- */|

65 | LinkAddress SweepBinsAndLink(void)| {| LinkAddress CurrentListHead;| int C;|

70 | CurrentListHead = null;| for (C = 'Z'; C>='A'; – – C) {| if (Front[C] != null) {| Link[Rear[C]] = CurrentListHead;| CurrentListHead = Front[C];

75 | }| }| return CurrentListHead;| }|

80 | /* ------------------------------------------------------------------------------- */|| void InitializeLinkedList(LinkAddress *ListHead)| {| int i;

Page 185: Standish Sol Manual Ece223

Table of Contents — continued

85 || Link[MaxIndex] = null;| for (i = (MaxIndex - 1); i>0; – – i) {| Link[i] = i+1;| }

90 | *ListHead = 1;| }|| /* ------------------------------------------------------------------------------- */|

95 | void InitializeBins(void)| {| int C;|| for (C = 'A'; C <= 'Z'; ++C) {

100 | Front[C] = null;| Rear[C] = null;| }| }|

105 | /* ------------------------------------------------------------------------------- */|

| void PrintList(SortingArray A, LinkAddress L)| {| int counter = 0;

110 || while (L != null) {| printf("%s ",A[L]);| if (((++counter)%11) == 0) {| putchar('\n'); /* print a return every 11 airport codes */

115 | }| L = Link[L];| }| printf("\n\n");| }

120 || /* ------------------------------------------------------------------------------- */|| LinkAddress RadixSort(SortingArray A)| {

125 | int k;| LinkAddress ListHead, CurrentNode;|| /* initialize the linked list and let ListHead point to first node */| InitializeLinkedList(&ListHead);

130 || /* make three passes from least to most significant radix digit k */|| for (k = 2; k>=0; – – k) {|

135 | /* initialize the bins to point to empty lists */| InitializeBins();|| /* scan the list and insert key-records into lists in the bins */| while (ListHead != null) {

140 | CurrentNode = ListHead;| ListHead = Link[ListHead];| Insert(CurrentNode,k);| }|

145 | /* sweep the bins and link them into a linked list */

Page 186: Standish Sol Manual Ece223

Table of Contents — continued

| ListHead = SweepBinsAndLink();| printf("pass number = %1d\n",3-k);| PrintList(A,ListHead);|

150 | }|| return ListHead; /* the procedure returns a pointer */| /* to the head of the sorted list */| }

155 || /* ------------------------------------------------------------------------------- */|| int main(void)| {

160 || /* Initialize the array A and print it */| InitializeLinkedList(&ListHead);| printf("Original Unsorted Array =\n");| PrintList(A,ListHead);

165 || /* Then RadixSort the array A and print it */| ListHead = RadixSort(A);| printf("Final Sorted Array =\n");| PrintList(A,ListHead);

170 | }| /* main program */|| /* -------------------------------------------------------------- */| /* when executed, the above program prints:

175 || Original Unsorted Array =| DUS MEX ORD RAL ABQ ONT NRT YYZ GLA ZRH BUF| DFW DEN ARN JAX BRU CRQ MIA SEA STL HOU SAN| GCM HKG PIT MKC SFO JFK ORY LGA ORL

180 || pass number = 1| GLA MIA SEA LGA MKC ORD BUF HKG ZRH JFK RAL| STL ORL GCM DEN ARN SAN SFO ABQ CRQ DUS ONT| NRT PIT BRU HOU DFW MEX JAX ORY YYZ

185 || pass number = 2| RAL SAN JAX ABQ GCM SEA DEN MEX JFK SFO DFW| LGA MIA PIT MKC HKG GLA ONT HOU ORD ZRH ORL| ARN CRQ NRT BRU ORY STL BUF DUS YYZ

190 || pass number = 3| ABQ ARN BRU BUF CRQ DEN DFW DUS GCM GLA HKG| HOU JAX JFK LGA MEX MIA MKC NRT ONT ORD ORL| ORY PIT RAL SAN SEA SFO STL YYZ ZRH

195 || Final Sorted Array =| ABQ ARN BRU BUF CRQ DEN DFW DUS GCM GLA HKG| HOU JAX JFK LGA MEX MIA MKC NRT ONT ORD ORL| ORY PIT RAL SAN SEA SFO STL YYZ ZRH

200 | */| /* -------------------------------------------------------------- */

2. The results below are printed by executing the program for Ex13.6.2 that follows the solution:

the original array A was: i = 0 1 2 3 4 5 6 7 8 9 10 11 12

Page 187: Standish Sol Manual Ece223

Table of Contents — continued

A[i]=[ 3.5, 12.3, 4.2, 1.5, 5.7, 12.6, 4.7, 7.2, 12.1, 2.9, 0.7, 8.1, 9.3]

hit counts: i = 0 1 2 3 4 5 6 7 8 9 10 11 12H[i] = 1 1 1 1 2 1 0 1 1 1 0 0 3

prox map:P[i] = 0 1 2 3 4 6 0 7 8 9 0 0 10

insertion locations:L[i] = 3 10 4 1 6 10 4 7 10 2 0 8 9 i = 0 1 2 3 4 5 6 7 8 9 10 11 12

| /*| * Ex. 13.6.2 -- Computing Hit Counts, Proxmap, and Insertion Locations| */|

5 | #include <stdio.h>| #include <stdlib.h>| #include <math.h>|| #define MaxIndex 13

10 || typedef long int LongInt;| typedef double KeyType; /* keys to be sorted are */| /* double precision floating point */|

15 | typedef enum {Empty, NotYetMoved, Moved} OccupantStatus;|| /* typedef enum {false, true} Boolean; */|| typedef struct {

20 | OccupantStatus Status;| int Proxmap, InsertionLoc;| KeyType Key;| }Slot;|

25 | typedef Slot SortingArray[MaxIndex];|| /* ------------------------------------------------------------------------------- */|| int MapKey(KeyType K) /* Maps key K into 0:MaxIndex – 1 */

30 | {| return (int) floor(K);| }|| /* -------------------------------------------------------------- */

35 || void PrintArray1(SortingArray A)| {| int i,K;|

40 | putchar('[');| for (i = 0; i < MaxIndex; ++i) {| K = MapKey(A[i].Key);| if ((A[i].Status == NotYetMoved) || (A[i].Status == Empty) ) {| printf("-----");

45 | } else {| printf("%5.1f",A[i].Key);| }| if (i<MaxIndex-1) putchar(',');| }

50 | printf("]");| }

Page 188: Standish Sol Manual Ece223

Table of Contents — continued

|| /* -------------------------------------------------------------- */|

55 | void ProxmapSort(SortingArray A)| {| int i, j, RunningTotal, TempInt;| KeyType KeyToInsert, TempKey;| Boolean NotInserted;

60 | char c;|| /* Initialize Status and Proxmap */| for (i = 0; i < MaxIndex; ++i) {| A[i].Proxmap = 0; /* init Proxmap to all zeroes */

65 | A[i].Status = NotYetMoved;| }|| /* Count hits when keys are mapped into insertion locations */| for (i = 0; i < MaxIndex; ++i) {

70 | j = MapKey(A[i].Key);| A[i].InsertionLoc = j;| A[j].Proxmap++;| }|

75 | /* Print hit counts */| printf("\nhit counts:\n");| for (i = 0; i < MaxIndex; ++i) printf("%6d",i);| putchar('\n');| for (i = 0; i < MaxIndex; ++i) printf("%6d",A[i].Proxmap);

80 | putchar('\n');|| /* Convert hit counts to a Proxmap */| RunningTotal = 0;| for (i = 0; i < MaxIndex; ++i) {

85 | if (A[i].Proxmap > 0) {| TempInt = A[i].Proxmap;| A[i].Proxmap = RunningTotal;| RunningTotal += TempInt;| }

90 | }|| /* Print Proxmap and check it */| printf("\nprox map:\n");| for (i = 0; i < MaxIndex; ++i) printf("%6d",A[i].Proxmap);

95 | putchar('\n');|| /* Compute InsertionLocs */| for (i = 0; i < MaxIndex; ++i) {| A[i].InsertionLoc = A[A[i].InsertionLoc].Proxmap;

100 | }|| /* Print insertion locations */| printf("\ninsertion locations:\n");| for (i = 0; i < MaxIndex; ++i) printf("%6d",A[i].InsertionLoc);

105 | putchar('\n');| for (i = 0; i < MaxIndex; ++i) printf("%6d",i);| putchar('\n');| PrintArray1(A);| putchar('\n');

110 || /* Now, A[i].InsertionLoc gives insertion location for A[i].Key */

Page 189: Standish Sol Manual Ece223

Table of Contents — continued

| /* and A[i].Status is NotYetMoved for all i in the address range */|| /* Rearrange A[i] in situ in A */

115 | for (i = 0; i < MaxIndex; ++i) {|| /* Find next key in ascending order of i that is NotYetMoved */| if (A[i].Status == NotYetMoved) {| j = A[i].InsertionLoc;

120 | KeyToInsert = A[i].Key;| A[i].Status = Empty;| NotInserted = true;|| while (NotInserted) {

125 | if (A[j].Status == NotYetMoved) {|| TempKey = A[j].Key;| A[j].Key = KeyToInsert;| KeyToInsert = TempKey;

130 | A[j].Status = Moved;| j = A[j].InsertionLoc;|| } else if (A[j].Status == Moved) {|

135 | if (KeyToInsert < A[j].Key) { /* If KeyToInsert < */| /* A[j].Key */| TempKey = A[j].Key; /* swap KeyToInsert */| A[j].Key = KeyToInsert; /* and A[j] */| KeyToInsert = TempKey;

140 || }|| j++; /* move to next key at A[j+1] */|

145 | } else { /* A[j].Status = Empty */|| A[j].Key = KeyToInsert;| A[j].Status = Moved;| NotInserted = false;

150 | }| PrintArray1(A);| c = getchar();| }| /* end while */

155 | }| /* end if */| }| /*end for */| }

160 | /* end ProxmapSort */|| /* -------------------------------------------------------------- */|| void PrintArray(SortingArray A)

165 | {| int i,K;|| for (i=0; i<MaxIndex; ++i) printf("%6d",i);| printf("\n[");

170 | for (i=0; i<MaxIndex; ++i) {| K = MapKey(A[i].Key);| printf("%5.1f",A[i].Key);| if (i < MaxIndex–1) putchar(',');

Page 190: Standish Sol Manual Ece223

Table of Contents — continued

| }175 | printf("]\n");

| }|| /* -------------------------------------------------------------- */|

180 | void InitArray(SortingArray A)| {| int i;| KeyType B[ ] = { 3.5, 12.3, 4.2, 1.5, 5.7, 12.6,| 4.7, 7.2, 12.1, 2.9, 0.7, 8.1, 9.3

185 | };|| for (i=0; i<MaxIndex; ++i) A[i].Key = B[i];| }|

190 | /* -------------------------------------------------------------- */|| int main(void)| {| SortingArray A;

195 || /* Initialization */| InitArray(A);| PrintArray(A);|

200 | /* sort the array A */| ProxmapSort(A);| PrintArray(A);| }| /* end main */

205 || /* -------------------------------------------------------------- */|| /* the printout was:|

210 | 0 1 2 3 4 5 6 7 8 9 10 11 12

| [ 3.5, 12.3, 4.2, 1.5, 5.7, 12.6, 4.7, 7.2, 12.1, 2.9, 0.7, 8.1, 9.3]

|| hit counts:| 0 1 2 3 4 5 6 7 8 9 10 11 12

215 | 1 1 1 1 2 1 0 1 1 1 0 0 3

|| prox map:| 0 1 2 3 4 6 0 7 8 9 0 0 10

|220 | insertion locations:

| 3 10 4 1 6 10 4 7 10 2 0 8 9

| progressive stages of in situ rearrangement in ProxmapSort:| 0 1 2 3 4 5 6 7 8 9 10 11 12

| [-----,-----,-----,-----,-----,-----,-----,-----,-----,-----,-----,-----,-----]

225 | [-----,-----,-----, 3.5,-----,-----,-----,-----,-----,-----,-----,-----,-----]

| [-----, 1.5,-----, 3.5,-----,-----,-----,-----,-----,-----,-----,-----,-----]

| [-----, 1.5,-----, 3.5,-----,-----,-----,-----,-----,-----, 12.3,-----,-----]

| [ 0.7, 1.5,-----, 3.5,-----,-----,-----,-----,-----,-----, 12.3,-----,-----]

| [ 0.7, 1.5,-----, 3.5, 4.2,-----,-----,-----,-----,-----, 12.3,-----,-----]

230 | [ 0.7, 1.5,-----, 3.5, 4.2,-----, 5.7,-----,-----,-----, 12.3,-----,-----]

| [ 0.7, 1.5,-----, 3.5, 4.2,-----, 5.7,-----,-----,-----, 12.3,-----,-----]

| [ 0.7, 1.5,-----, 3.5, 4.2, 4.7, 5.7,-----,-----,-----, 12.3,-----,-----]

| [ 0.7, 1.5,-----, 3.5, 4.2, 4.7, 5.7,-----,-----,-----, 12.3,-----,-----]

| [ 0.7, 1.5,-----, 3.5, 4.2, 4.7, 5.7,-----,-----,-----, 12.3, 12.6,-----]

Page 191: Standish Sol Manual Ece223

Table of Contents — continued

235 | [ 0.7, 1.5,-----, 3.5, 4.2, 4.7, 5.7,-----, 8.1,-----, 12.3, 12.6,-----]

| [ 0.7, 1.5,-----, 3.5, 4.2, 4.7, 5.7,-----, 8.1,-----, 12.1, 12.6,-----]

| [ 0.7, 1.5,-----, 3.5, 4.2, 4.7, 5.7,-----, 8.1,-----, 12.1, 12.3,-----]

| [ 0.7, 1.5,-----, 3.5, 4.2, 4.7, 5.7,-----, 8.1,-----, 12.1, 12.3, 12.6]

| [ 0.7, 1.5,-----, 3.5, 4.2, 4.7, 5.7,-----, 8.1, 9.3, 12.1, 12.3, 12.6]

240 | [ 0.7, 1.5, 2.9, 3.5, 4.2, 4.7, 5.7,-----, 8.1, 9.3, 12.1, 12.3, 12.6]

| [ 0.7, 1.5, 2.9, 3.5, 4.2, 4.7, 5.7, 7.2, 8.1, 9.3, 12.1, 12.3, 12.6]

| 0 1 2 3 4 5 6 7 8 9 10 11 12

| [ 0.7, 1.5, 2.9, 3.5, 4.2, 4.7, 5.7, 7.2, 8.1, 9.3, 12.1, 12.3, 12.6]

|245 | */

| /* -------------------------------------------------------------- */

Answers to Review Questions 13.71. ShellSort applies insertion sorting to subsequences of keys spaced

delta spaces apart, for a sequence of deltas diminishing eventually to1, at which time ordinary insertion sorting is applied. For example,if de l ta == 3, then She l lSo r t insertion sorts the subsequencesconsisting of every third key starting at A [0 ] , every third keystarting at A[1] , and every third key starting at A[2] . A possiblesequence of deltas to use to sort A[0:n–1] is to start with delta = 1 + n/3 ,and to diminish del ta, using delta = 1 + n/3 ; on each pass untileventually delta == 1, at which time ShellSort terminates. ShellSort iscalled a diminishing increment method because the deltas used asincrement intervals, diminish systematically to 1.

2. BubbleSort makes repeated passes through the array of keys to besorted, by scanning (A[i], A[i+1]) for i in 0:MaxIndex–2. It transposes (i.e.,exchanges) any pair that is out of sorted order (i.e., for which A[i] >

A[i+1]). It terminates after making the first pass during which noout-of-order pairs were detected. It is called a t r anspos i t i onsorting method because it works by transposing adjacent out-of-order pairs of keys.

3. BubbleSort runs in time O(n2).

Solutions to Selected Exercises in Exercises 13.71. See the function FasterBubbleSort(A) in the program below.

2. See the function CocktailShakerSort(A) in the program below.

| /*| * Ex. 13.7.1 & 13.7.2 -- Three Versions of BubbleSort including| * FasterBubbleSort & CocktailShakerSort| */

5 || #include <stdio.h>| #include <stdlib.h>|| /* -------------------------------------------------- */

10 || void BubbleSort(SortingArray A)

Page 192: Standish Sol Manual Ece223

Table of Contents — continued

| {| int i;| KeyType Temp;

15 | Boolean NotFinished;|| do {| NotFinished = false;| for (i = 0; i < MaxIndex – 1; ++i) {

20 | if (A[i] > A[i+1]) {| /* exchange A[i] and A[i+1] */| Temp = A[i]; A[i] = A[i+1]; A[i+1]=Temp;| /* if you made an exchange you are not finished and */| /* you need another pass */

25 | NotFinished = true;| }| }| } while (NotFinished); /* NotFinished = false iff you made a */| /* pass and no pair of keys was out of order */

30 | }|| /* -------------------------------------------------- */|| void FasterBubbleSort(SortingArray A)

35 | {| int i, UpperIndex;| KeyType Temp;| Boolean NotFinished;|

40 | UpperIndex = MaxIndex – 2;|| do {| NotFinished = false;| for (i = 0; i <= UpperIndex; ++i) {

45 | if (A[i] > A[i+1]) {| /* exchange A[i] and A[i+1] */| Temp = A[i]; A[i] = A[i+1]; A[i+1]=Temp;| /* if you made an exchange you are not finished and */| /* you need another pass */

50 | NotFinished = true;| }| }| UpperIndex – –;| } while (NotFinished); /* NotFinished = false iff you made a */

55 | /* pass and no pair of keys was out of order */| }|| /* -------------------------------------------------- */|

60 | void CocktailShakerSort(SortingArray A)| {| int i, LowerIndex, UpperIndex;| KeyType Temp;| Boolean NotFinished;

65 || LowerIndex = 0;| UpperIndex = MaxIndex – 2;|| do {

70 | NotFinished = false;| for (i = LowerIndex; i <= UpperIndex; ++i) {| if (A[i] > A[i+1]) {| /* exchange A[i] and A[i+1] */

Page 193: Standish Sol Manual Ece223

Table of Contents — continued

| Temp = A[i]; A[i] = A[i+1]; A[i+1]=Temp;75 | /* if you made an exchange you are not finished and */

| /* you need another pass */| NotFinished = true;| }| }

80 | UpperIndex – – ;| for (i = UpperIndex; i >=LowerIndex; – – i) {| if (A[i] > A[i+1]) {| /* exchange A[i] and A[i+1] */| Temp = A[i]; A[i] = A[i+1]; A[i+1]=Temp;

85 | /* if you made an exchange you are not finished and */| /* you need another pass */| NotFinished = true;| }| }

90 | LowerIndex++;| } while (NotFinished); /* NotFinished = false iff you made a */| /* pass and no pair of keys was out of order */| }|

95 | /* -------------------------------------------------- */|| /*| The Table below give the results in Ticks for an average| running time of five runs of each variant of BubbleSort on

100 | the array sizes given at the left of the table.|| Array Normal Faster Cocktail

| Size BubbleSort BubbleSort ShakerSort

| -----------------------------------------------------

105 | 64 7.2 4.6 3.8

| 128 33.0 19.2 16.4

| 256 131.8 76.0 60.6

| 512 543.2 305.4 242.4

| 1024 2234.2 1229.6 972.4

110 | -----------------------------------------------------

|

| It is seen that CocktailShakerSort is 26% faster than| the faster BubbleSort, and 230% faster than normal| BubbleSort, for arrays of size 1024.

115 || */|| /* -------------------------------------------------- */

Answers to Review Questions 13.81. InsertionSort is the best of the O(n2) sorting methods compared in

Table 13.47.

2. QuickSort is the best of the O(n log n) sorting methods compared inTable 13.47.

3. Sometimes special cases arise in which a direct efficient methodof sorting can be applied, such as when we are given an array A[0:n–1 ] containing a permutation of the numbers in the range

Page 194: Standish Sol Manual Ece223

Table of Contents — continued

100:100+n–1. In this case, we need only store A[i] in location A[A[i]– 100] (for 0 ≤ i ≤ n–1) to sort the array.

Solutions to Selected Exercises in Exercises 13.81. Solution not given.

2. BubbleSor t because its first pass is its last pass and it does notmove any keys.

3. Solution not given.

Page 195: Standish Sol Manual Ece223

Table of Contents — continued

Answers to Review Questions 14.21. A context-free grammar (CFG) is a 4-tuple (S, VN, VT, P), where VN

and VT are finite sets of distinct symbols (such that VN ∩ VT = ∅ )called the non-terminal symbols (VN) and the terminal symbols(VT) respectively, where S ∈  VN is a distinguished symbol of VN

called the start symbol, and where P is a set of productions of theform x → α, such that x ∈ VN and such that α is a string of symbols inV T ∪  VN.

2. Let x → α be a production in a CFG. We say x → α is d i rec t l yrecursive if the string α contains x, and we say x → α is indirectlyrecursive if α contains a non-terminal symbol, say y, such that ycan derive to a string containing x using other productions of theCFG.

3. A r ightmost derivation of a sentence γ with respect to a context-free grammar G = (S, VN, VT, P) is a derivation of γ starting with S,in which only the r i g h t m o s t non-terminal symbol of eachsentential form is derived at each step of the derivation.

4. The language L(G) generated by the context-free grammar G is theset of all sentences derived in G starting with the start symbol Sof G.

Solutions to Selected Exercises in Exercises 14.2

1. Starting with E apply productionE → E + T

to rewrite it as E + T T → Τ ∗ FE + T*F F → PE + T*P P → aE + T*a T → FE + F*a F → PE + P*a P → aE + a*a E → TT + a*a T → FF + a*a F → F ↑ PF↑ P + a*a P → aF↑ a + a*a F → PP↑ a + a*a P → ( E )( E )↑ a + a*a E → E – T(E – T)↑ a + a*a T → F(E – F)↑ a + a*a F → P(E – P)↑ a + a*a P → a(E – a)↑ a + a*a E → T(T – a)↑ a + a*a T → F(F – a)↑ a + a*a F → P(P – a)↑ a + a*a P → a

Page 196: Standish Sol Manual Ece223

Table of Contents — continued

(a – a) ↑ a + a*a

2. A new extended grammar which generates expressions having aunary minus is as follows:

E → E + T | E – T | F

T → T * F | T / F | F

F → F ↑ U | U

U → – P | + P | P

P → ( E ) | a where a → <letter> | <digit>

3. Define a struct type for binary tree nodes as follows:

typedef struct TreeNodeTag {KeyType Key;struct TreeNodeTag *LeftLink;struct TreeNodeTag *RightLink;

} TreeNode;

Then you can declare a variable N suitable for containing a pointerto a TreeNode as follows:

TreeNode *N;

Then you can store a pointer to a TreeNode in N , which, in turn,contains TreeNode pointers (or N U L L pointers) as its LeftLink andRightLink members. Non-null LeftLink and RightLink TreeNode pointers can,in turn, point to other T r e e N o d e s having subtrees of finite, butunbounded, shape. Thus, the struct typedef given aboverecursively defines an unbounded set of linked binary tree datastructures using a finite definition.

Answers to Review Question 14.31. The process of parsing a sentence is the process of discovering

its grammatical structure with respect to a grammar G, byproducing a derivation of the sentence in G. This can be done byprogressively collapsing the sentence into the start symbol of G,using repeated replacement of various substrings α of thesentence (or of partially collapsed sentential forms) with non-terminals N, where N → α is an appropriate production of thegrammar G.

2. The parser of Program 14.6 uses a set of four functions that cancall one another in a cyclic fashion, and hence which can makerecursive calls on themselves indirectly, using a chain of calls onthe others. Such a set of functions is said to be mutually recursive.In particular, in Program 14.6, ParseP calls ParseE, which calls ParseT,

Page 197: Standish Sol Manual Ece223

Table of Contents — continued

which calls ParseF, which calls ParseP in a cyclic fashion. In symbols,we could write: ParseP ⊃ ParseE ⊃ ParseT ⊃ ParseF ⊃ ParseP, where thesymbol “⊃ ” means “calls.”

3. Rightmost derivations and leftmost parses are inverses in thesense that when a rightmost derivation (in which rightmost non-terminals are replaced by right sides of productions) is readbackwards, it gives a plan for replacing a leftmost subexpression,which is equal to the right side of some production, with the non-terminal on the left side of that production — yielding an instanceof a leftmost parse.

Solutions to Selected Exercises in Exercises 14.31. A C module ParserUtil it ies.c, which implements the low-level utility

calls in Program 14.6 is given as follows:

| /*| * Ex. 14.3.1 – – the "ParserUtilities.c" module| *| * This module exports parser utility services. It uses the same sequential

5 | * stack representation as is used for Program 7.5, the parenthesis matching| * program in Chapter 7.| */|| #include "ParserUtilities.h"

10 || /******| *| * The ParserUtilities.h file contains the following external declarations:| *

15 | *****/|| /*|| #include <stdio.h>

20 | #include <stdlib.h>| #include <string.h>| #include <ctype.h>| #include "SeqStackInterface.h"|

25 | /+ The last line above includes the following from +/| /+ the "SeqStackInterface.h" file: +/|| typedef StackType Stack; /+ the StackType will depend on the +/| /+ representation +/

30 | extern void InitializeStack (Stack *S);| /+ Initialize the stack S to be the empty stack +/|| extern int Empty (Stack *S);| /+ Returns TRUE if and only if the stack S is empty +/

35 || extern int Full (Stack *S);| /+ Returns TRUE if and only if the stack S is full +/|| extern void Push (ItemType X, Stack *S);

40 | /+ If S is not full push a new item X onto the top of stack S +/

Page 198: Standish Sol Manual Ece223

Table of Contents — continued

|| extern void Pop (Stack *S, ItemType *X);| /+ If S is non-empty, pop an item off the top of the stack S +/| /+ and put it in X +/

45 || /+ -------------------< a typedef exported by the module >----------------------- +/|| extern typedef enum {false, true} Boolean;|

50 | /+ ---------------< the only variable exported by the module >------------------- +/|| extern char Next; /+ the next character in the input string +/|| /+ --------------< the four procedures exported by the module >----------------- +/

55 || extern void GetInputStringFromUser(void); /+ gets the Input +/| /+ string from the user +/|| extern void Reduce(int n, char S); /+ pops n items off Stack, +/

60 | /+ then pushes S on Stack +/|| extern void Shift(char c); /+ reads c from Input and +/| /+ pushes c on Stack.+/|

65 | extern Boolean UserWantsToContinue(void); /+ returns "true" iff +/| /+ user wants to continue.+/|| /+ ------------------------------------------------------------------------------- +/|

70 | */| /* ----<< the implementation part of the module >>---- */|| /* --------------------< variables private to the module>------------------------- */|

75 | char Next; /* the next character in the input string */|| char| *Answer = "blank", /* a reply from the user */| *InputString /* the input string */

80 | = "a blank input string long enough for an expression";|| Stack InputStack, ParseStack;|| /* -------------------< functions defined in the module >------------------------ */

85 || void GetInputStringFromUser(void) /* gets an Input */| { /* string from user to parse */| int i;|

90 | InitializeStack(&ParseStack); /* initialize the ParseStack */| / to the empty stack */| InitializeStack(&InputStack); /* initialize the InputStack */| /* to the empty stack */| Push('#',&InputStack); /* put a 'pad' character */

95 | /* at bottom of InputStack */| printf("give input string: ");| gets(InputString); /* scanf("%s",InputString); */|| for (i = strlen(InputString) – 1; i >= 0; – – i) {

100 | Push(InputString[i],&InputStack);| }| Next = '#';

Page 199: Standish Sol Manual Ece223

Table of Contents — continued

| Shift('#'); /* get things started */| }

105 || /* ------------------------------------------------------------------------------- */|| char FetchNext(void) /* returns next non-blank */| { /* character in the Input */

110 | char d, f;|| d = Next; /* find the next non-space character in the Input */|| do { /* find the next non-space character in the Input */

115 | Pop(&InputStack, &f);| } while (f == ' ');|| Next = f; /* let Next be this next non-space */| /* character in the Input */

120 || return d; /* return d as the value of the function */| }|| /* ------------------------------------------------------------------------------- */

125 || void PrintErrorMessageIfNecessary(char c, char d)| {|| /* if d isn't equal to the expected character c, write an error message. */

130 || if (c == 'a') { /* recall that 'a' stands for any <letter> or <digit> */|| if (! (islower(d) | | isdigit(d)) ) {| printf("Syntax Error: expected <letter> or <digit> but got %c\n", d);

135 | }|| } else if (c != d) {|| printf("Syntax Error: expected %c but got %c\n", c, d);

140 | }|| }|| /* ------------------------------------------------------------------------------- */

145 || void Shift(char c)| {| char d; /* d holds the character Shifted from the Input string */|

150 | d = FetchNext( ); /* read the next character d from */| /* the Input string */| Push(d,&ParseStack); /* push d on top of ParseStack */| PrintErrorMessageIfNecessary(c,d); /* if c != d then */| /* print Shift error message */

155 | }|| /* ------------------------------------------------------------------------------- */|| void Reduce(int n, char S) /* pop n items from Stack, push S */

160 | {| int i; char *Output;|| /* write the reduction: top n items of stack – – > S, and */| /* pop n chars off stack. */

Page 200: Standish Sol Manual Ece223

Table of Contents — continued

165 || Output = " ";|| Output[2*n] = '\0';| for (i = n – 1; i >= 0; – – i) {

170 | Pop(&ParseStack,&Output[2*i]);| Output[2*i+1] = ' ';| }|| if (n == 1) {

175 || if (S == 'P') {|| /* PFstring = Concat(PFString,Output); */|

180 | printf(" %c – – > a { where a = %s}\n", S, Output);| /* later for use in Program 14.8 */| /* add," %s", . . . , PFString) */| } else {|

185 | printf(" %c – – > %s\n",S,Output);|| }|| } else {

190 || printf(" %c – – > %s\n", S, Output);|| /* later for use in Program 14.8, add:| if ((n == 3) && (S != 'P') ) {

195 || PFstring = Concat(PFstring, Output[3],' ');|| printf(" %s\n", PFString);|

200 | } else {| putchar('\n');| }| */| }

205 | /* end if */|| /* push S on stack */|| Push(S,&ParseStack);

210 || }| /* end Reduce */|| /* ------------------------------------------------------------------------------- */

215 || int UserWantsToContinue(void)| {| printf("Do you want to give another string? (y/n) ");| gets(Answer); /* scanf("%s",Answer); */

220 | return (Answer[0] == 'y');| }|| /* -----------------------------< end of ParserUtilities.c >----------------------------- */|

Page 201: Standish Sol Manual Ece223

Table of Contents — continued

2. The productions for G1 are {E → 0 E 0, E → 1 E 1, E → 2}. Thecorresponding railroad diagram for E is:

A function for a recursive-descent Parser for E is:

| /*| * Solution to Ex.14.3.2 -- Marked Palindrome Parser| */|

5 | #include "ParserUtilities.h"|| extern char Next; /* the next character in the input string */|| /* ------------------------------------------------------------------------------- */

10 || void ParseE(void) /* parse a marked palindrome */| {| if (Next == '2') {| Shift('2');

15 | Reduce(1,'E');| } else if (Next == '0') {| Shift('0');| ParseE( );| Shift('0');

20 | Reduce(3,'E');| } else if (Next == '1') {| Shift('1');| ParseE( );| Shift('1');

25 | Reduce(3,'E');| }| }|| /* ------------------------------------------------------------------------------- */

30 || int main(void)| {| do {| GetInputStringFromUser( );

35 | ParseE( ); /* call the parser to parse the input string. */| } while ( UserWantsToContinue( ) );| }

Page 202: Standish Sol Manual Ece223

Table of Contents — continued

3. It is not possible to write a left-to-right, shift-reduce, recursivedescent parser with a finite lookahead that parses unmarkedpalindromes in the language generated by the grammar G2.

4 G3 = { E → 1 1 0, E → 1 1 E 0 }.

5. The grammar cannot generate any sentences, which are sententialforms containing only terminal symbols, because every sententialform generated by the grammar contains exactly one non-terminalsymbol. Hence L(G) = ∅ .

Answers to Selected Review Questions 14.41. The infix-to-postfix translator given in Program 14.8 is a modified

left-to-right, shift-reduce parser for infix expressions that hasbeen extended to produce postfix output. There is an initiallyempty postfix output string stored as the value of the variablePostfixString and there is a procedure AppendToOutput(x), which concaten-ates the string x onto the right end of the PostfixString. Each of theoriginal procedures ParseP, ParseF, ParseT, and ParseE, is a self-contained unit of action which parses respectively, an instance of aP, F, T, or E, in the input string, and appends its respective postfixtranslation P , F , T , or E , to the output stored as the value ofPostfixString. In so doing, each parsing routine may make calls on theothers and may depend on their output as units of action used toaccomplish its own task. For example, ParseT may try to parse a T*Fas a T and may call P a r s e F first to parse the initial T which isgrammatically forced to become an F in T*F. and may then callParseF again to parse the rightmost F in T*F. However, beforecalling ParseF the second time, the multiplication operator (*) issaved as the value of the local variable Operator in ParseT. Then afterP a r s e F has been called twice, the multiplication operator isappended to the end of PostfixString, using AppendToOutput(Operator). Theresult is to append T F * onto the end of the Postf ixStr ing as thepostfix translation of the infix T*F (where T is the postfixtranslation of T obtained by calling ParseF and reducing its parseroutput F to a T, and where F is the postfix translation of F obtainedby calling Parse F).

2. The main theme used in the infix-to-postfix translator is: “Totranslate an expression containing a binary infix operator intopostfix, first parse the infix subexpression for the left operandand append its postfix translation to the end of the output string,save the binary operator, parse the infix subexpression for theright operand and append its postfix translation to the end of theoutput string, and finally, append the operator to the end of theoutput str ing.” The translations of the left and right

Page 203: Standish Sol Manual Ece223

Table of Contents — continued

subexpressions are performed by making appropriate calls onfunctions to parse and translate the expected types of operandsubexpressions.

3. Yes. For instance, ParseE calls Parse T, which calls ParseF, which callsParseP, which calls ParseE in a cycle. Hence a call to any of ParseE,ParseT, ParseF, or ParseP can generate an indirect recursive call toitself via a chain of intermediate calls to the others.

Solutions to Selected Exercises in Exercises 14.41. The solutions to Exs. 14.4.1 and 14.4.2 are contained in the

following program:

| /****| *| * Exs 14.4.1 & 14.4.2 -- Augmented Infix-to-Postfix translator & evaluator| * -- which handles unary minus

5 | *| ***/|| /****| *

10 | * To complete the module "YourCalculationModule" of Ex. 14.4.2 add a| * headerfile "YourCalculationModule.h" to the current ".c" file containing| * the extern declaration:| *| * extern char *Evaluate(char *);

15 | *| * as shown in Program 4.12 on p. 109| *| ***/|

20 | /****| *| * This program augments the Recursive Descent Parser of Program 14.6| * to handle unary minus operators according to the grammar of Ex 14.2.2| * shown immediately below, translates infix to postfix, evaluates the

25 | * postfix, and prints the string representing the floating point value.| *| * E --> E + T | E – T | T| * T --> T * F | T / F | F| * F --> F ^ U | U

30 | * U --> – P | + P | P| * P --> ( E ) | a| *| * Here, we let 'a' stand for any letter or digit:| *

35 | * a --> <letter> | <digit>| *| * The grammar above uses E, T, F, and P to stand for the following| * "standard" abbreviations in the literature:| *

40 | * E = Expression, T = Term, F = Factor, and P = Primary.| *| * In addition, it uses U to stand for a Unary operand.| *| ****/

Page 204: Standish Sol Manual Ece223

Table of Contents — continued

45 || #include "ParserUtilities.h"| #include "EvalStackInterface.h"||

50 | /* -< The ParserUtilities.h file contains the following external declarations >- */|| /*|| #include <stdio.h>

55 | #include <stdlib.h>| #include <math.h>| #include <ctype.h>| #include <string.h>|

60 | /+ ---------------< the four procedures exported by the module >---------------- +/||| extern void GetInputStringFromUser(void); /+ gets the Input string +/| /+ from the user +/

65 || extern void Reduce(int n, char *S); /+ pops n items off Stack, +/| /+ then pushes S on Stack +/|| extern void Shift(char *c); /+ reads c from Input and +/

70 | /+ pushes c on Stack +/|| extern Boolean UserWantsToContinue(void); /+ returns "true" iff user +/| /+ wants to continue +/|

75 | extern void AppendToOutput(char X); /+ appends X to postfix +/| /+ output string +/|| /+ ----------------------------------------------------------------------------- +/|

80 | */||| /* ------< three external variables imported from ParserUtilities.c >--------- */|

85 | extern char Next; /* the next character in the input string */|| extern char *PostfixString; /* the postfix output string */|| extern char *InputString; /* the user-supplied input string */

90 || /* ---< an external variable imported from EvalStackImplementation.c >--- */|| extern Stack EvalStack; /* where ItemType = float */|

95 | /* --------------------< a global variable for this module >--------------------- */|| char *ResultString = "00000000000000000000"; /* the string */| /* for the value of the output */|

100 | /* -------------< the internal functions of the module begin here >--------------- */|| extern void ParseE(void); /* extern because function */| /* called before defined */|

105 | /* ------------------------------------------------------------------------------- */|

Page 205: Standish Sol Manual Ece223

Table of Contents — continued

| void ParseP(void) /* parse a Primary */| {| char Operand;

110 || if (Next == '(') {| Shift('(');| ParseE( );| Shift(')');

115 | Reduce(3,'P');| } else {| Operand = Next;| Shift('a'); /* 'a' stands for any <letter> or <digit> */| Reduce(1,'P');

120 | AppendToOutput(Operand);| }| }|| /* ------------------------------------------------------------------------------- */

125 || void ParseU(void) /* parse a Unary */| {| char Operator;|

130 | if ( (Next == '+') || (Next == '–') ) {| Operator = Next;| Shift(Next);| ParseP( );| Reduce(2,'U');

135 | if (Operator == '–') {| AppendToOutput('~'); /* appends unary minus */| } /* operator */| } else {| ParseP( );

140 | Reduce(1,'U');| }| }|| /* ------------------------------------------------------------------------------- */

145 || void ParseF(void) /* parse a Factor */| {| char Operator;|

150 | ParseU( );| Reduce(1,'F');| while (Next == '^') {| Operator = Next;| Shift(Next);

155 | ParseU( );| Reduce(3,'F');| AppendToOutput(Operator);| }| }

160 || /* ------------------------------------------------------------------------------- */|| void ParseT(void) /* parse a Term */| {

165 | char Operator;|| ParseF( );| Reduce(1,'T');

Page 206: Standish Sol Manual Ece223

Table of Contents — continued

| while ( (Next == '*') || (Next == '/') ) {170 | Operator = Next;

| Shift(Next);| ParseF( );| Reduce(3,'T');| AppendToOutput(Operator);

175 | }| }|| /* ------------------------------------------------------------------------------- */|

180 | void ParseE(void) /* parse an Expression */| {| char Operator;|| ParseT( );

185 | Reduce(1,'E');| while ( (Next == '+') || (Next == '–') ) {| Operator = Next;| Shift(Next);| ParseT( );

190 | Reduce(3,'E');| AppendToOutput(Operator);| }| }|

195 | /* ------------------------------------------------------------------------------- */|| void InterpretPostfix(void)| {| float LeftOperand, RightOperand, Result;

200 || int i; /* the index of the ith character in the PostfixString */|| char c; /* c = ith character of the input string */|

205 | char *s = "x"; /* s will hold a null-terminated string in which */| /* s[0] will hold c, for use in an atof conversion */|| InitializeStack(&EvalStack);|

210 | for (i = 0; i < strlen(PostfixString); ++i) {|| s[0] = c = PostfixString[i]; /* s[0] = c = ith character of */| /* input string */| if (isdigit(c)) { /* if c is a digit, push c's value onto stack */

215 || if ( Full(&EvalStack) ) {| printf("Stack full. Postfix Evaluation could not be completed.\n");| return;| } else {

220 | Push((float)atof(s),&EvalStack);| }|| } else if (c=='~') { /* handle case of unary minus operator */|

225 | if ( Empty(&EvalStack) ) {| printf("Malformed postfix input string. Too many operators\n");| printf("and too few operands.\n");| return;| } else {

230 | Pop(&EvalStack,&RightOperand);

Page 207: Standish Sol Manual Ece223

Table of Contents — continued

| Push( –RightOperand, &EvalStack);| }||

235 | } else if (c=='+' || c=='–' || c=='*' || c=='/' || c=='^') {|| if ( Empty(&EvalStack) ) {| printf("Malformed postfix input string. Too many operators\n");| printf("and too few operands.\n");

240 | return;| } else {| Pop(&EvalStack,&RightOperand);| if ( Empty(&EvalStack) ) {| printf("Malformed postfix input string. Too many\n");

245 | printf("operators and too few operands.\n");| return;| } else {| Pop(&EvalStack, &LeftOperand);|

250 | switch (c) { /* perform the operation */|| case '+': Push(LeftOperand+RightOperand,&EvalStack);| break;| case '–': Push(LeftOperand–RightOperand,&EvalStack);

255 | break;| case '*': Push(LeftOperand*RightOperand,&EvalStack);| break;| case '/': if (RightOperand != 0.0) {|

Push(LeftOperand/RightOperand,&EvalStack);260 | } else {

| printf("Attempt to divide by zero.\n");| return;| }| break;

265 | case '^': Push(exp(log(LeftOperand)*RightOperand),| &EvalStack);| break;| default: break;| }

270 | }| }| } else {| printf("Illegal character '%c' in postfix expression.\n",c);| return;

275 | }| } /* end for */||| Pop(&EvalStack,&Result); /* remove final result from stack */

280 || if ( Empty(&EvalStack) ) {| sprintf(ResultString,"%f",Result); /*convert float result */| } else { /* to string output */| printf("Malformed postfix string.\n");

285 | printf("Too many operands and not enough operators.\n");| }| }|| /* -----< the following function is exported by YourCalculationModule >----- */

290 || char *Evaluate(char *S)

Page 208: Standish Sol Manual Ece223

Table of Contents — continued

| {| strcpy(InputString,S);| ParseE(); /* call the parser to parse the input string. */

295 | InterpretPostfix();| return ResultString;| }|| /* --< the main function tests the operation of YourCalculationModule >-- */

300 || int main(void)| {|| do {

305 | PostfixString[0] = '\0'; /* initialize PostfixString to empty string */| GetInputStringFromUser( );| ResultString = Evaluate(InputString);| printf("output = %s\n",ResultString);| putchar('\n');

310 | } while ( UserWantsToContinue( ) );|| }| /* end main program */

Answers to Review Questions 14.51. The first is : To prove P(n) is true for all non-negative integers n ≥

0, (1) First, prove P(0) is true, and then, (2) Assuming P(n) is truefor n, prove P(n+1) is true.

The second is: To prove P(n) is true for all integers n ≥ 0, (1) First,prove P(0) is true, and then, (2) Assuming P(k) is true for 0 ≤ k < n,prove P(n) is true.

2. Recursion induction is a method that can be used to prove thatrecursive programs correctly produce their intended results byfirst proving that base cases are correct, and then, on theassumption that recursive calls correctly solve smallersubproblems and eventually result in calls to base cases, byproving that the recursive programs themselves correctlyproduce their intended results.

3. First, for the m base cases of sizes n1, n2, ... , nm, prove F(t1), F(t2),... , F(tm) satisfy P(n1), P(n2), ... , P(nm) respectively. Then, on theassumption that F(t) calls itself recursively with calls F(tj) onproblems of smaller size n j (eventually leading to base cases),show that F(t) produces results that enable you to prove that P(n)is true.

Solutions to Selected Exercises in Exercises 14.51. When you attempt to apply recursion induction to prove that

f(n ) = 1, you encounter the difficulty that recursive calls on fwithin f sometimes make calls on subproblems of larger size, as inthe cases where n is odd when f(n ) calls f(3*n +1). Thus, the

Page 209: Standish Sol Manual Ece223

Table of Contents — continued

assumption that all calls on subproblems of smaller size yieldcorrect results is of no use in constructing the proof in caseswhere the subproblems in the recursive calls are of larger sizebecause, in such cases, this assumption doesn’t apply.

2. The Partition algorithm given as Program 13.15 does not alwaysmeet the requirement that i == j+1 after the call to Partition(A, &i, &j).Hence, a redesigned partition algorithm, such as that given below,must be used, which guarantees that A[m:n] is partitioned into A[m:j]

and A[i:n] where i == j+1 and m ≤ j < n after returning from the callPartition(A, m, n) and after setting i and j appropriately. On thatassumption, we turn to the proof that the Find function given belowcorrectly moves the kth item of A[m:n] into the kth position, where 1 ≤k ≤ n–m+1 initially.

For the base case, suppose k == 1 and m == n. Then the callFind(A,k,m,n) does nothing to rearrange A (because the condition m < n

on line 28 is false) and the kth (i.e., the first) item of A[m:n] = A[m].

Now suppose A[m:n] contains more than one item, so that m < n, andsuppose, by recursion induction, that Find(A,k,m,n) works correctlyfor all arrays with fewer than n–m+1 items.

The partition algorithm is called with Partition(A, m, n) to partitionA[m:n] into a left partition A[m:j] and a right partition A[i:n], where i ==

j+1 and m ≤ j < n. Lines 30:36 of the Find function below set i and j

appropriately to ensure that these conditions are met. Theelements in the left partition will be less than or equal to thesmallest element in the right partition after returning from thepartition function call. If now, k ≤ (j–m+1), it means that there are atleast k elements in the left partition (recalling that the number ofelements in A[m: j ] is j – m + 1 according to Eq. A.5 of the “MathReference appendix”). Since all the items in the left partition aresmaller than any of the items in the right partition and there are k

or more items in the left partition, the kth smallest item in A[m:n] isfound by taking the kth smallest item in the left partition, which, byrecursion induction, is correctly found by recursively callingF ind (A ,k ,m, j ) on line 39 of the F i n d function given below. (It isguaranteed that A[m:j] is a smaller array than A[m:n] because j < n afterthe call to Partition(A,m,n) and after the computations to set i and j onlines 30:36 of Find below. Hence, the recursion induction conditionapplies in this case.)

Now suppose that, after returning from the call Partition(A,m,n) andafter the computations to set i and j on lines 30:36 of Find below, itis false that k ≤ (j–m+1), meaning that there are fewer than k

elements in A[m:j]. Then because all j–m+1 items in A[m:j] are less thanor equal to each of the items in A[i:n], the kth smallest item in A[m:n] is

Page 210: Standish Sol Manual Ece223

Table of Contents — continued

not to be found among the items in the left partition but instead isto be found among the items in the right partition. Because (j–m+1)

of the smallest items of the original A[m:n] are already in the leftpartition, the kth smallest item of A[m:n] is obtained by finding the k –

( j–m+1) th item of the right partition A[i :n] where i  ==  j+1. Again byrecursion induction, since A[i:n] contains fewer items than A[m:n], thecall Find(A,k–(j–m+1),i,n) on line 41 correctly finds the k–(j–m+1)th item ofA[i:n] which is the same as the kth smallest item of A[m:n].

| /*--------------------------------------------------------------------------*/|| int Partition(ItemArray A, int i, int j) /* assume i < j */| {

5 | ItemType Pivot, Temp; /* the value returned by the Partition */| int k, middle, p; /* function is the location of the Pivot */| /* after partitioning */| middle = (i+j)/2;| Pivot = A[middle]; A[middle] = A[i]; A[i] = Pivot;

10 | p = i;|| for (k= i+1; k <= j; ++k) {| if (A[k] < Pivot) {| Temp = A[++p]; A[p] = A[k]; A[k] = Temp;

15 | }| }|| Temp = A[i]; A[i] = A[p]; A[p] = Temp;| return p;

20 | }|| /*--------------------------------------------------------------------------*/|| void Find(ItemArray A, int k, int m, int n) /* to move the kth smallest */

25 | { /* item in A[m:n] into the kth position */| int i, j, p;|| if (m < n) { /* if there is more than one item in A to partition */|

30 | p = Partition(A, m, n); /* p == location of the Pivot after partition */|| if (p == n) { /* ensure that the partition is of the form */| j = p–1; i = p; /* A[m:j] and A[i:n], where i == j+1 */| } else { /* and ensure that m≤j<n */

35 | j = p; i = p+1;| }|| if ( k <= (j – m + 1) ) { /* if A[m:j] has at least k items, then */| Find(A, k, m, j); /* Find kth in A[m:j] */

40 | } else { /* otherwise, */| Find(A, k–(j–m+1), i, n); /* Find k–(j–m+1)th in A[i:n] */| }| }| }

45 || /*--------------------------------------------------------------------------*/

Page 211: Standish Sol Manual Ece223

Table of Contents — continued

3. Assume A [0 :n–1 ] is an array of n distinct objects, and let therecursion induction hypothesis for calling P e r m ( A , m , n ) be thefollowing:

Perm(A,m,n) prints all distinct permutations of A[0:n–1] withA[m:n–1] fixed and A[0:m–1] varying.

For the base case, m == 0, we need to show that Perm(A,0,n) prints allpermutations of A[0:n–1] with A[0:n–1] fixed and A[0:–1] varying. Therange 0:–1 is empty (because by definition i:j == { k | i ≤ k ≤ j}, so 0:–1 == { k

| 0 ≤ k ≤ –1} == ∅ ). Hence there is only one permutation of A[0:n–1] withA[0:n–1] fixed and A[0:–1] == ∅ varying, and it is obtained by printingA[0:n–1]. But, when m == 0, Perm(A,0,n) calls PrintPerm(A,n) which simplyprints the items in A [0 :n–1 ] (see lines 13:21 and 38:39 of theprogram below). Hence, for m == 0, Perm(A,m,n) prints A[0:n–1] which iswhat we needed to show.

Now assume that Perm(A,k,n) prints all the permutations with A[k:n–

1] fixed and A[0:k–1] varying for all k < n. In Perm(A,m,n), when m == n, foreach i in 0:n–1 Perm(A,n,n) exchanges A[i] with A[n–1], then calls Perm(A,n–

1,n) to print all permutations of A[0:n–1] with A[n–1] fixed and A[0:n–2]

varying, and exchanges A[n–1] and A[i] back again. The effect of thisis to choose each of the distinct objects in A[0:n–1] as the fixedobject A[n–1] while printing all permutations with the remainingA [ 0 : n – 2 ] objects varying (which must occur correctly by therecursion induction hypothesis using the recursive call Perm(A,n–1,n)

on a problem of size k == n–1 where k < n).

Therefore, calling Perm(A,n,n) prints all distinct permutations ofA[0:n–1].

| /*------------------------------------------------------------*/|| void Exchange(ItemArray A, int i, int j)| {

5 | ItemType temp;|| temp = A[i]; A[i] = A[j]; A[j] = temp;|| }

10 || /*------------------------------------------------------------*/|| void PrintPerm(ItemArray A, int n)| {

15 | int i;|| for (i = 0; i < n; ++i) {| printf("%2d, ",A[i]);| }

20 | putchar('\n');| }|| /*------------------------------------------------------------*/

Page 212: Standish Sol Manual Ece223

Table of Contents — continued

|25 | void InitPermArray(ItemArray A, int n)

| {| int i;|| for (i=0; i<n; ++i) A[i]=i+1;

30 | }|| /*------------------------------------------------------------*/|| void Perm(ItemArray A, int m, int n)

35 | {| int i;|| if (m == 0) {| PrintPerm(A, n);

40 | } else {| for (i = 0; i < m; ++i) {| Exchange(A, i, m–1);| Perm(A, m–1, n);| Exchange(A, m–1, i);

45 | }| }| }|| /*------------------------------------------------------------*/

50 || void Permute(ItemArray A, int n)| {| InitPermArray(A,n);| Perm(A,n,n);

55 | }|| /*------------------------------------------------------------*/

Page 213: Standish Sol Manual Ece223

Table of Contents — continued

Answers to Review Questions 14.21. A context-free grammar (CFG) is a 4-tuple (S, VN, VT, P), where VN

and VT are finite sets of distinct symbols (such that VN ∩ VT = ∅ )called the non-terminal symbols (VN) and the terminal symbols(VT) respectively, where S ∈  VN is a distinguished symbol of VN

called the start symbol, and where P is a set of productions of theform x → α, such that x ∈ VN and such that α is a string of symbols inV T ∪  VN.

2. Let x → α be a production in a CFG. We say x → α is d i rec t l yrecursive if the string α contains x, and we say x → α is indirectlyrecursive if α contains a non-terminal symbol, say y, such that ycan derive to a string containing x using other productions of theCFG.

3. A r ightmost derivation of a sentence γ with respect to a context-free grammar G = (S, VN, VT, P) is a derivation of γ starting with S,in which only the r i g h t m o s t non-terminal symbol of eachsentential form is derived at each step of the derivation.

4. The language L(G) generated by the context-free grammar G is theset of all sentences derived in G starting with the start symbol Sof G.

Solutions to Selected Exercises in Exercises 14.2

1. Starting with E apply productionE → E + T

to rewrite it as E + T T → Τ ∗ FE + T*F F → PE + T*P P → aE + T*a T → FE + F*a F → PE + P*a P → aE + a*a E → TT + a*a T → FF + a*a F → F ↑ PF↑ P + a*a P → aF↑ a + a*a F → PP↑ a + a*a P → ( E )( E )↑ a + a*a E → E – T(E – T)↑ a + a*a T → F(E – F)↑ a + a*a F → P(E – P)↑ a + a*a P → a(E – a)↑ a + a*a E → T(T – a)↑ a + a*a T → F(F – a)↑ a + a*a F → P(P – a)↑ a + a*a P → a

Page 214: Standish Sol Manual Ece223

Table of Contents — continued

(a – a) ↑ a + a*a

2. A new extended grammar which generates expressions having aunary minus is as follows:

E → E + T | E – T | F

T → T * F | T / F | F

F → F ↑ U | U

U → – P | + P | P

P → ( E ) | a where a → <letter> | <digit>

3. Define a struct type for binary tree nodes as follows:

typedef struct TreeNodeTag {KeyType Key;struct TreeNodeTag *LeftLink;struct TreeNodeTag *RightLink;

} TreeNode;

Then you can declare a variable N suitable for containing a pointerto a TreeNode as follows:

TreeNode *N;

Then you can store a pointer to a TreeNode in N , which, in turn,contains TreeNode pointers (or N U L L pointers) as its LeftLink andRightLink members. Non-null LeftLink and RightLink TreeNode pointers can,in turn, point to other T r e e N o d e s having subtrees of finite, butunbounded, shape. Thus, the struct typedef given aboverecursively defines an unbounded set of linked binary tree datastructures using a finite definition.

Answers to Review Question 14.31. The process of parsing a sentence is the process of discovering

its grammatical structure with respect to a grammar G, byproducing a derivation of the sentence in G. This can be done byprogressively collapsing the sentence into the start symbol of G,using repeated replacement of various substrings α of thesentence (or of partially collapsed sentential forms) with non-terminals N, where N → α is an appropriate production of thegrammar G.

2. The parser of Program 14.6 uses a set of four functions that cancall one another in a cyclic fashion, and hence which can makerecursive calls on themselves indirectly, using a chain of calls onthe others. Such a set of functions is said to be mutually recursive.In particular, in Program 14.6, ParseP calls ParseE, which calls ParseT,

Page 215: Standish Sol Manual Ece223

Table of Contents — continued

which calls ParseF, which calls ParseP in a cyclic fashion. In symbols,we could write: ParseP ⊃ ParseE ⊃ ParseT ⊃ ParseF ⊃ ParseP, where thesymbol “⊃ ” means “calls.”

3. Rightmost derivations and leftmost parses are inverses in thesense that when a rightmost derivation (in which rightmost non-terminals are replaced by right sides of productions) is readbackwards, it gives a plan for replacing a leftmost subexpression,which is equal to the right side of some production, with the non-terminal on the left side of that production — yielding an instanceof a leftmost parse.

Solutions to Selected Exercises in Exercises 14.31. A C module ParserUtil it ies.c, which implements the low-level utility

calls in Program 14.6 is given as follows:

| /*| * Ex. 14.3.1 – – the "ParserUtilities.c" module| *| * This module exports parser utility services. It uses the same sequential

5 | * stack representation as is used for Program 7.5, the parenthesis matching| * program in Chapter 7.| */|| #include "ParserUtilities.h"

10 || /******| *| * The ParserUtilities.h file contains the following external declarations:| *

15 | *****/|| /*|| #include <stdio.h>

20 | #include <stdlib.h>| #include <string.h>| #include <ctype.h>| #include "SeqStackInterface.h"|

25 | /+ The last line above includes the following from +/| /+ the "SeqStackInterface.h" file: +/|| typedef StackType Stack; /+ the StackType will depend on the +/| /+ representation +/

30 | extern void InitializeStack (Stack *S);| /+ Initialize the stack S to be the empty stack +/|| extern int Empty (Stack *S);| /+ Returns TRUE if and only if the stack S is empty +/

35 || extern int Full (Stack *S);| /+ Returns TRUE if and only if the stack S is full +/|| extern void Push (ItemType X, Stack *S);

40 | /+ If S is not full push a new item X onto the top of stack S +/

Page 216: Standish Sol Manual Ece223

Table of Contents — continued

|| extern void Pop (Stack *S, ItemType *X);| /+ If S is non-empty, pop an item off the top of the stack S +/| /+ and put it in X +/

45 || /+ -------------------< a typedef exported by the module >----------------------- +/|| extern typedef enum {false, true} Boolean;|

50 | /+ ---------------< the only variable exported by the module >------------------- +/|| extern char Next; /+ the next character in the input string +/|| /+ --------------< the four procedures exported by the module >----------------- +/

55 || extern void GetInputStringFromUser(void); /+ gets the Input +/| /+ string from the user +/|| extern void Reduce(int n, char S); /+ pops n items off Stack, +/

60 | /+ then pushes S on Stack +/|| extern void Shift(char c); /+ reads c from Input and +/| /+ pushes c on Stack.+/|

65 | extern Boolean UserWantsToContinue(void); /+ returns "true" iff +/| /+ user wants to continue.+/|| /+ ------------------------------------------------------------------------------- +/|

70 | */| /* ----<< the implementation part of the module >>---- */|| /* --------------------< variables private to the module>------------------------- */|

75 | char Next; /* the next character in the input string */|| char| *Answer = "blank", /* a reply from the user */| *InputString /* the input string */

80 | = "a blank input string long enough for an expression";|| Stack InputStack, ParseStack;|| /* -------------------< functions defined in the module >------------------------ */

85 || void GetInputStringFromUser(void) /* gets an Input */| { /* string from user to parse */| int i;|

90 | InitializeStack(&ParseStack); /* initialize the ParseStack */| / to the empty stack */| InitializeStack(&InputStack); /* initialize the InputStack */| /* to the empty stack */| Push('#',&InputStack); /* put a 'pad' character */

95 | /* at bottom of InputStack */| printf("give input string: ");| gets(InputString); /* scanf("%s",InputString); */|| for (i = strlen(InputString) – 1; i >= 0; – – i) {

100 | Push(InputString[i],&InputStack);| }| Next = '#';

Page 217: Standish Sol Manual Ece223

Table of Contents — continued

| Shift('#'); /* get things started */| }

105 || /* ------------------------------------------------------------------------------- */|| char FetchNext(void) /* returns next non-blank */| { /* character in the Input */

110 | char d, f;|| d = Next; /* find the next non-space character in the Input */|| do { /* find the next non-space character in the Input */

115 | Pop(&InputStack, &f);| } while (f == ' ');|| Next = f; /* let Next be this next non-space */| /* character in the Input */

120 || return d; /* return d as the value of the function */| }|| /* ------------------------------------------------------------------------------- */

125 || void PrintErrorMessageIfNecessary(char c, char d)| {|| /* if d isn't equal to the expected character c, write an error message. */

130 || if (c == 'a') { /* recall that 'a' stands for any <letter> or <digit> */|| if (! (islower(d) | | isdigit(d)) ) {| printf("Syntax Error: expected <letter> or <digit> but got %c\n", d);

135 | }|| } else if (c != d) {|| printf("Syntax Error: expected %c but got %c\n", c, d);

140 | }|| }|| /* ------------------------------------------------------------------------------- */

145 || void Shift(char c)| {| char d; /* d holds the character Shifted from the Input string */|

150 | d = FetchNext( ); /* read the next character d from */| /* the Input string */| Push(d,&ParseStack); /* push d on top of ParseStack */| PrintErrorMessageIfNecessary(c,d); /* if c != d then */| /* print Shift error message */

155 | }|| /* ------------------------------------------------------------------------------- */|| void Reduce(int n, char S) /* pop n items from Stack, push S */

160 | {| int i; char *Output;|| /* write the reduction: top n items of stack – – > S, and */| /* pop n chars off stack. */

Page 218: Standish Sol Manual Ece223

Table of Contents — continued

165 || Output = " ";|| Output[2*n] = '\0';| for (i = n – 1; i >= 0; – – i) {

170 | Pop(&ParseStack,&Output[2*i]);| Output[2*i+1] = ' ';| }|| if (n == 1) {

175 || if (S == 'P') {|| /* PFstring = Concat(PFString,Output); */|

180 | printf(" %c – – > a { where a = %s}\n", S, Output);| /* later for use in Program 14.8 */| /* add," %s", . . . , PFString) */| } else {|

185 | printf(" %c – – > %s\n",S,Output);|| }|| } else {

190 || printf(" %c – – > %s\n", S, Output);|| /* later for use in Program 14.8, add:| if ((n == 3) && (S != 'P') ) {

195 || PFstring = Concat(PFstring, Output[3],' ');|| printf(" %s\n", PFString);|

200 | } else {| putchar('\n');| }| */| }

205 | /* end if */|| /* push S on stack */|| Push(S,&ParseStack);

210 || }| /* end Reduce */|| /* ------------------------------------------------------------------------------- */

215 || int UserWantsToContinue(void)| {| printf("Do you want to give another string? (y/n) ");| gets(Answer); /* scanf("%s",Answer); */

220 | return (Answer[0] == 'y');| }|| /* -----------------------------< end of ParserUtilities.c >----------------------------- */|

Page 219: Standish Sol Manual Ece223

Table of Contents — continued

2. The productions for G1 are {E → 0 E 0, E → 1 E 1, E → 2}. Thecorresponding railroad diagram for E is:

A function for a recursive-descent Parser for E is:

| /*| * Solution to Ex.14.3.2 -- Marked Palindrome Parser| */|

5 | #include "ParserUtilities.h"|| extern char Next; /* the next character in the input string */|| /* ------------------------------------------------------------------------------- */

10 || void ParseE(void) /* parse a marked palindrome */| {| if (Next == '2') {| Shift('2');

15 | Reduce(1,'E');| } else if (Next == '0') {| Shift('0');| ParseE( );| Shift('0');

20 | Reduce(3,'E');| } else if (Next == '1') {| Shift('1');| ParseE( );| Shift('1');

25 | Reduce(3,'E');| }| }|| /* ------------------------------------------------------------------------------- */

30 || int main(void)| {| do {| GetInputStringFromUser( );

35 | ParseE( ); /* call the parser to parse the input string. */| } while ( UserWantsToContinue( ) );| }

Page 220: Standish Sol Manual Ece223

Table of Contents — continued

3. It is not possible to write a left-to-right, shift-reduce, recursivedescent parser with a finite lookahead that parses unmarkedpalindromes in the language generated by the grammar G2.

4 G3 = { E → 1 1 0, E → 1 1 E 0 }.

5. The grammar cannot generate any sentences, which are sententialforms containing only terminal symbols, because every sententialform generated by the grammar contains exactly one non-terminalsymbol. Hence L(G) = ∅ .

Answers to Selected Review Questions 14.41. The infix-to-postfix translator given in Program 14.8 is a modified

left-to-right, shift-reduce parser for infix expressions that hasbeen extended to produce postfix output. There is an initiallyempty postfix output string stored as the value of the variablePostfixString and there is a procedure AppendToOutput(x), which concaten-ates the string x onto the right end of the PostfixString. Each of theoriginal procedures ParseP, ParseF, ParseT, and ParseE, is a self-contained unit of action which parses respectively, an instance of aP, F, T, or E, in the input string, and appends its respective postfixtranslation P , F , T , or E , to the output stored as the value ofPostfixString. In so doing, each parsing routine may make calls on theothers and may depend on their output as units of action used toaccomplish its own task. For example, ParseT may try to parse a T*Fas a T and may call P a r s e F first to parse the initial T which isgrammatically forced to become an F in T*F. and may then callParseF again to parse the rightmost F in T*F. However, beforecalling ParseF the second time, the multiplication operator (*) issaved as the value of the local variable Operator in ParseT. Then afterP a r s e F has been called twice, the multiplication operator isappended to the end of PostfixString, using AppendToOutput(Operator). Theresult is to append T F * onto the end of the Postf ixStr ing as thepostfix translation of the infix T*F (where T is the postfixtranslation of T obtained by calling ParseF and reducing its parseroutput F to a T, and where F is the postfix translation of F obtainedby calling Parse F).

2. The main theme used in the infix-to-postfix translator is: “Totranslate an expression containing a binary infix operator intopostfix, first parse the infix subexpression for the left operandand append its postfix translation to the end of the output string,save the binary operator, parse the infix subexpression for theright operand and append its postfix translation to the end of theoutput string, and finally, append the operator to the end of theoutput str ing.” The translations of the left and right

Page 221: Standish Sol Manual Ece223

Table of Contents — continued

subexpressions are performed by making appropriate calls onfunctions to parse and translate the expected types of operandsubexpressions.

3. Yes. For instance, ParseE calls Parse T, which calls ParseF, which callsParseP, which calls ParseE in a cycle. Hence a call to any of ParseE,ParseT, ParseF, or ParseP can generate an indirect recursive call toitself via a chain of intermediate calls to the others.

Solutions to Selected Exercises in Exercises 14.41. The solutions to Exs. 14.4.1 and 14.4.2 are contained in the

following program:

| /****| *| * Exs 14.4.1 & 14.4.2 -- Augmented Infix-to-Postfix translator & evaluator| * -- which handles unary minus

5 | *| ***/|| /****| *

10 | * To complete the module "YourCalculationModule" of Ex. 14.4.2 add a| * headerfile "YourCalculationModule.h" to the current ".c" file containing| * the extern declaration:| *| * extern char *Evaluate(char *);

15 | *| * as shown in Program 4.12 on p. 109| *| ***/|

20 | /****| *| * This program augments the Recursive Descent Parser of Program 14.6| * to handle unary minus operators according to the grammar of Ex 14.2.2| * shown immediately below, translates infix to postfix, evaluates the

25 | * postfix, and prints the string representing the floating point value.| *| * E --> E + T | E – T | T| * T --> T * F | T / F | F| * F --> F ^ U | U

30 | * U --> – P | + P | P| * P --> ( E ) | a| *| * Here, we let 'a' stand for any letter or digit:| *

35 | * a --> <letter> | <digit>| *| * The grammar above uses E, T, F, and P to stand for the following| * "standard" abbreviations in the literature:| *

40 | * E = Expression, T = Term, F = Factor, and P = Primary.| *| * In addition, it uses U to stand for a Unary operand.| *| ****/

Page 222: Standish Sol Manual Ece223

Table of Contents — continued

45 || #include "ParserUtilities.h"| #include "EvalStackInterface.h"||

50 | /* -< The ParserUtilities.h file contains the following external declarations >- */|| /*|| #include <stdio.h>

55 | #include <stdlib.h>| #include <math.h>| #include <ctype.h>| #include <string.h>|

60 | /+ ---------------< the four procedures exported by the module >---------------- +/||| extern void GetInputStringFromUser(void); /+ gets the Input string +/| /+ from the user +/

65 || extern void Reduce(int n, char *S); /+ pops n items off Stack, +/| /+ then pushes S on Stack +/|| extern void Shift(char *c); /+ reads c from Input and +/

70 | /+ pushes c on Stack +/|| extern Boolean UserWantsToContinue(void); /+ returns "true" iff user +/| /+ wants to continue +/|

75 | extern void AppendToOutput(char X); /+ appends X to postfix +/| /+ output string +/|| /+ ----------------------------------------------------------------------------- +/|

80 | */||| /* ------< three external variables imported from ParserUtilities.c >--------- */|

85 | extern char Next; /* the next character in the input string */|| extern char *PostfixString; /* the postfix output string */|| extern char *InputString; /* the user-supplied input string */

90 || /* ---< an external variable imported from EvalStackImplementation.c >--- */|| extern Stack EvalStack; /* where ItemType = float */|

95 | /* --------------------< a global variable for this module >--------------------- */|| char *ResultString = "00000000000000000000"; /* the string */| /* for the value of the output */|

100 | /* -------------< the internal functions of the module begin here >--------------- */|| extern void ParseE(void); /* extern because function */| /* called before defined */|

105 | /* ------------------------------------------------------------------------------- */|

Page 223: Standish Sol Manual Ece223

Table of Contents — continued

| void ParseP(void) /* parse a Primary */| {| char Operand;

110 || if (Next == '(') {| Shift('(');| ParseE( );| Shift(')');

115 | Reduce(3,'P');| } else {| Operand = Next;| Shift('a'); /* 'a' stands for any <letter> or <digit> */| Reduce(1,'P');

120 | AppendToOutput(Operand);| }| }|| /* ------------------------------------------------------------------------------- */

125 || void ParseU(void) /* parse a Unary */| {| char Operator;|

130 | if ( (Next == '+') || (Next == '–') ) {| Operator = Next;| Shift(Next);| ParseP( );| Reduce(2,'U');

135 | if (Operator == '–') {| AppendToOutput('~'); /* appends unary minus */| } /* operator */| } else {| ParseP( );

140 | Reduce(1,'U');| }| }|| /* ------------------------------------------------------------------------------- */

145 || void ParseF(void) /* parse a Factor */| {| char Operator;|

150 | ParseU( );| Reduce(1,'F');| while (Next == '^') {| Operator = Next;| Shift(Next);

155 | ParseU( );| Reduce(3,'F');| AppendToOutput(Operator);| }| }

160 || /* ------------------------------------------------------------------------------- */|| void ParseT(void) /* parse a Term */| {

165 | char Operator;|| ParseF( );| Reduce(1,'T');

Page 224: Standish Sol Manual Ece223

Table of Contents — continued

| while ( (Next == '*') || (Next == '/') ) {170 | Operator = Next;

| Shift(Next);| ParseF( );| Reduce(3,'T');| AppendToOutput(Operator);

175 | }| }|| /* ------------------------------------------------------------------------------- */|

180 | void ParseE(void) /* parse an Expression */| {| char Operator;|| ParseT( );

185 | Reduce(1,'E');| while ( (Next == '+') || (Next == '–') ) {| Operator = Next;| Shift(Next);| ParseT( );

190 | Reduce(3,'E');| AppendToOutput(Operator);| }| }|

195 | /* ------------------------------------------------------------------------------- */|| void InterpretPostfix(void)| {| float LeftOperand, RightOperand, Result;

200 || int i; /* the index of the ith character in the PostfixString */|| char c; /* c = ith character of the input string */|

205 | char *s = "x"; /* s will hold a null-terminated string in which */| /* s[0] will hold c, for use in an atof conversion */|| InitializeStack(&EvalStack);|

210 | for (i = 0; i < strlen(PostfixString); ++i) {|| s[0] = c = PostfixString[i]; /* s[0] = c = ith character of */| /* input string */| if (isdigit(c)) { /* if c is a digit, push c's value onto stack */

215 || if ( Full(&EvalStack) ) {| printf("Stack full. Postfix Evaluation could not be completed.\n");| return;| } else {

220 | Push((float)atof(s),&EvalStack);| }|| } else if (c=='~') { /* handle case of unary minus operator */|

225 | if ( Empty(&EvalStack) ) {| printf("Malformed postfix input string. Too many operators\n");| printf("and too few operands.\n");| return;| } else {

230 | Pop(&EvalStack,&RightOperand);

Page 225: Standish Sol Manual Ece223

Table of Contents — continued

| Push( –RightOperand, &EvalStack);| }||

235 | } else if (c=='+' || c=='–' || c=='*' || c=='/' || c=='^') {|| if ( Empty(&EvalStack) ) {| printf("Malformed postfix input string. Too many operators\n");| printf("and too few operands.\n");

240 | return;| } else {| Pop(&EvalStack,&RightOperand);| if ( Empty(&EvalStack) ) {| printf("Malformed postfix input string. Too many\n");

245 | printf("operators and too few operands.\n");| return;| } else {| Pop(&EvalStack, &LeftOperand);|

250 | switch (c) { /* perform the operation */|| case '+': Push(LeftOperand+RightOperand,&EvalStack);| break;| case '–': Push(LeftOperand–RightOperand,&EvalStack);

255 | break;| case '*': Push(LeftOperand*RightOperand,&EvalStack);| break;| case '/': if (RightOperand != 0.0) {|

Push(LeftOperand/RightOperand,&EvalStack);260 | } else {

| printf("Attempt to divide by zero.\n");| return;| }| break;

265 | case '^': Push(exp(log(LeftOperand)*RightOperand),| &EvalStack);| break;| default: break;| }

270 | }| }| } else {| printf("Illegal character '%c' in postfix expression.\n",c);| return;

275 | }| } /* end for */||| Pop(&EvalStack,&Result); /* remove final result from stack */

280 || if ( Empty(&EvalStack) ) {| sprintf(ResultString,"%f",Result); /*convert float result */| } else { /* to string output */| printf("Malformed postfix string.\n");

285 | printf("Too many operands and not enough operators.\n");| }| }|| /* -----< the following function is exported by YourCalculationModule >----- */

290 || char *Evaluate(char *S)

Page 226: Standish Sol Manual Ece223

Table of Contents — continued

| {| strcpy(InputString,S);| ParseE(); /* call the parser to parse the input string. */

295 | InterpretPostfix();| return ResultString;| }|| /* --< the main function tests the operation of YourCalculationModule >-- */

300 || int main(void)| {|| do {

305 | PostfixString[0] = '\0'; /* initialize PostfixString to empty string */| GetInputStringFromUser( );| ResultString = Evaluate(InputString);| printf("output = %s\n",ResultString);| putchar('\n');

310 | } while ( UserWantsToContinue( ) );|| }| /* end main program */

Answers to Review Questions 14.51. The first is : To prove P(n) is true for all non-negative integers n ≥

0, (1) First, prove P(0) is true, and then, (2) Assuming P(n) is truefor n, prove P(n+1) is true.

The second is: To prove P(n) is true for all integers n ≥ 0, (1) First,prove P(0) is true, and then, (2) Assuming P(k) is true for 0 ≤ k < n,prove P(n) is true.

2. Recursion induction is a method that can be used to prove thatrecursive programs correctly produce their intended results byfirst proving that base cases are correct, and then, on theassumption that recursive calls correctly solve smallersubproblems and eventually result in calls to base cases, byproving that the recursive programs themselves correctlyproduce their intended results.

3. First, for the m base cases of sizes n1, n2, ... , nm, prove F(t1), F(t2),... , F(tm) satisfy P(n1), P(n2), ... , P(nm) respectively. Then, on theassumption that F(t) calls itself recursively with calls F(tj) onproblems of smaller size n j (eventually leading to base cases),show that F(t) produces results that enable you to prove that P(n)is true.

Solutions to Selected Exercises in Exercises 14.51. When you attempt to apply recursion induction to prove that

f(n ) = 1, you encounter the difficulty that recursive calls on fwithin f sometimes make calls on subproblems of larger size, as inthe cases where n is odd when f(n ) calls f(3*n +1). Thus, the

Page 227: Standish Sol Manual Ece223

Table of Contents — continued

assumption that all calls on subproblems of smaller size yieldcorrect results is of no use in constructing the proof in caseswhere the subproblems in the recursive calls are of larger sizebecause, in such cases, this assumption doesn’t apply.

2. The Partition algorithm given as Program 13.15 does not alwaysmeet the requirement that i == j+1 after the call to Partition(A, &i, &j).Hence, a redesigned partition algorithm, such as that given below,must be used, which guarantees that A[m:n] is partitioned into A[m:j]

and A[i:n] where i == j+1 and m ≤ j < n after returning from the callPartition(A, m, n) and after setting i and j appropriately. On thatassumption, we turn to the proof that the Find function given belowcorrectly moves the kth item of A[m:n] into the kth position, where 1 ≤k ≤ n–m+1 initially.

For the base case, suppose k == 1 and m == n. Then the callFind(A,k,m,n) does nothing to rearrange A (because the condition m < n

on line 28 is false) and the kth (i.e., the first) item of A[m:n] = A[m].

Now suppose A[m:n] contains more than one item, so that m < n, andsuppose, by recursion induction, that Find(A,k,m,n) works correctlyfor all arrays with fewer than n–m+1 items.

The partition algorithm is called with Partition(A, m, n) to partitionA[m:n] into a left partition A[m:j] and a right partition A[i:n], where i ==

j+1 and m ≤ j < n. Lines 30:36 of the Find function below set i and j

appropriately to ensure that these conditions are met. Theelements in the left partition will be less than or equal to thesmallest element in the right partition after returning from thepartition function call. If now, k ≤ (j–m+1), it means that there are atleast k elements in the left partition (recalling that the number ofelements in A[m: j ] is j – m + 1 according to Eq. A.5 of the “MathReference appendix”). Since all the items in the left partition aresmaller than any of the items in the right partition and there are k

or more items in the left partition, the kth smallest item in A[m:n] isfound by taking the kth smallest item in the left partition, which, byrecursion induction, is correctly found by recursively callingF ind (A ,k ,m, j ) on line 39 of the F i n d function given below. (It isguaranteed that A[m:j] is a smaller array than A[m:n] because j < n afterthe call to Partition(A,m,n) and after the computations to set i and j onlines 30:36 of Find below. Hence, the recursion induction conditionapplies in this case.)

Now suppose that, after returning from the call Partition(A,m,n) andafter the computations to set i and j on lines 30:36 of Find below, itis false that k ≤ (j–m+1), meaning that there are fewer than k

elements in A[m:j]. Then because all j–m+1 items in A[m:j] are less thanor equal to each of the items in A[i:n], the kth smallest item in A[m:n] is

Page 228: Standish Sol Manual Ece223

Table of Contents — continued

not to be found among the items in the left partition but instead isto be found among the items in the right partition. Because (j–m+1)

of the smallest items of the original A[m:n] are already in the leftpartition, the kth smallest item of A[m:n] is obtained by finding the k –

( j–m+1) th item of the right partition A[i :n] where i  ==  j+1. Again byrecursion induction, since A[i:n] contains fewer items than A[m:n], thecall Find(A,k–(j–m+1),i,n) on line 41 correctly finds the k–(j–m+1)th item ofA[i:n] which is the same as the kth smallest item of A[m:n].

| /*--------------------------------------------------------------------------*/|| int Partition(ItemArray A, int i, int j) /* assume i < j */| {

5 | ItemType Pivot, Temp; /* the value returned by the Partition */| int k, middle, p; /* function is the location of the Pivot */| /* after partitioning */| middle = (i+j)/2;| Pivot = A[middle]; A[middle] = A[i]; A[i] = Pivot;

10 | p = i;|| for (k= i+1; k <= j; ++k) {| if (A[k] < Pivot) {| Temp = A[++p]; A[p] = A[k]; A[k] = Temp;

15 | }| }|| Temp = A[i]; A[i] = A[p]; A[p] = Temp;| return p;

20 | }|| /*--------------------------------------------------------------------------*/|| void Find(ItemArray A, int k, int m, int n) /* to move the kth smallest */

25 | { /* item in A[m:n] into the kth position */| int i, j, p;|| if (m < n) { /* if there is more than one item in A to partition */|

30 | p = Partition(A, m, n); /* p == location of the Pivot after partition */|| if (p == n) { /* ensure that the partition is of the form */| j = p–1; i = p; /* A[m:j] and A[i:n], where i == j+1 */| } else { /* and ensure that m≤j<n */

35 | j = p; i = p+1;| }|| if ( k <= (j – m + 1) ) { /* if A[m:j] has at least k items, then */| Find(A, k, m, j); /* Find kth in A[m:j] */

40 | } else { /* otherwise, */| Find(A, k–(j–m+1), i, n); /* Find k–(j–m+1)th in A[i:n] */| }| }| }

45 || /*--------------------------------------------------------------------------*/

Page 229: Standish Sol Manual Ece223

Table of Contents — continued

3. Assume A [0 :n–1 ] is an array of n distinct objects, and let therecursion induction hypothesis for calling P e r m ( A , m , n ) be thefollowing:

Perm(A,m,n) prints all distinct permutations of A[0:n–1] withA[m:n–1] fixed and A[0:m–1] varying.

For the base case, m == 0, we need to show that Perm(A,0,n) prints allpermutations of A[0:n–1] with A[0:n–1] fixed and A[0:–1] varying. Therange 0:–1 is empty (because by definition i:j == { k | i ≤ k ≤ j}, so 0:–1 == { k

| 0 ≤ k ≤ –1} == ∅ ). Hence there is only one permutation of A[0:n–1] withA[0:n–1] fixed and A[0:–1] == ∅ varying, and it is obtained by printingA[0:n–1]. But, when m == 0, Perm(A,0,n) calls PrintPerm(A,n) which simplyprints the items in A [0 :n–1 ] (see lines 13:21 and 38:39 of theprogram below). Hence, for m == 0, Perm(A,m,n) prints A[0:n–1] which iswhat we needed to show.

Now assume that Perm(A,k,n) prints all the permutations with A[k:n–

1] fixed and A[0:k–1] varying for all k < n. In Perm(A,m,n), when m == n, foreach i in 0:n–1 Perm(A,n,n) exchanges A[i] with A[n–1], then calls Perm(A,n–

1,n) to print all permutations of A[0:n–1] with A[n–1] fixed and A[0:n–2]

varying, and exchanges A[n–1] and A[i] back again. The effect of thisis to choose each of the distinct objects in A[0:n–1] as the fixedobject A[n–1] while printing all permutations with the remainingA [ 0 : n – 2 ] objects varying (which must occur correctly by therecursion induction hypothesis using the recursive call Perm(A,n–1,n)

on a problem of size k == n–1 where k < n).

Therefore, calling Perm(A,n,n) prints all distinct permutations ofA[0:n–1].

| /*------------------------------------------------------------*/|| void Exchange(ItemArray A, int i, int j)| {

5 | ItemType temp;|| temp = A[i]; A[i] = A[j]; A[j] = temp;|| }

10 || /*------------------------------------------------------------*/|| void PrintPerm(ItemArray A, int n)| {

15 | int i;|| for (i = 0; i < n; ++i) {| printf("%2d, ",A[i]);| }

20 | putchar('\n');| }|| /*------------------------------------------------------------*/

Page 230: Standish Sol Manual Ece223

Table of Contents — continued

|25 | void InitPermArray(ItemArray A, int n)

| {| int i;|| for (i=0; i<n; ++i) A[i]=i+1;

30 | }|| /*------------------------------------------------------------*/|| void Perm(ItemArray A, int m, int n)

35 | {| int i;|| if (m == 0) {| PrintPerm(A, n);

40 | } else {| for (i = 0; i < m; ++i) {| Exchange(A, i, m–1);| Perm(A, m–1, n);| Exchange(A, m–1, i);

45 | }| }| }|| /*------------------------------------------------------------*/

50 || void Permute(ItemArray A, int n)| {| InitPermArray(A,n);| Perm(A,n,n);

55 | }|| /*------------------------------------------------------------*/

Page 231: Standish Sol Manual Ece223

Table of Contents — continued

Answers to Review Questions 15.21. If theTObject is a variable which can contain a pointer to an instance

of an object of class TClass, we perform the assignment:theTObject = new TClass;

to create an instance of an object of type TClass and to assign it tobe the value of the variable theTObject. Recall that new X allocates aninstance of a new object of class X in the heap and returns apointer to that object instance.

2. An object instance is analogous to a struct in C which has two kindsof members: data members and function members. The datamembers are called the object’s instance variables and containdata values local to the object instance. The function members,which are called the object’s methods, are local operators thatapply to the object instance and that can manipulate values of itsinstance variables.

3. In C++ it is possible to define a class C2 as a derived class of abase class C1. When you do this, the object instances of class C2

inherit the instance variables and methods of the base class C1.Inheritance means that every instance of an object of class C2 hasall the instance variables and methods defined for objects that areinstances of the base class C1. When you customize an object classC 2, you define some features local to C2 that differ from thefeatures inherited from the base class C1. In effect, this allows youto program using differences by saying that the object instancesin the derived class C2 are exactly like those in C1 except thatthey have some local differences (i.e., local instance variables andlocal methods) defined by customization. Sometimes a base classis set up as an abstract class that will never have any actual objectinstances. New derived classes are formed as subclasses of thisabstract base class by customization. For example, a class TShape

could be defined as an abstract base class having a virtual Draw( )

method. New classes, such as TRectangle, TSquare, TCircle and TOval

could be defined as derived classes of the class TShape each havingits own actual distinct Draw( ) method. Nobody will ever create anactual instance of the abstract class TShape, although individualinstances of each of the derived classes, TRectangle, TSquare, TCircle

and TOval, will be created and will be drawn. The virtual methodDraw( ) defined for the class TShape functions, so-to-speak, as a“placeholder method” that gives a common method name tocustomize, and thus to share, among the identically-named Draw( )

methods of derived classes having particular shapes that canactually be drawn. Supplying actual executable Draw( ) methods for

Page 232: Standish Sol Manual Ece223

Table of Contents — continued

each derived class customizes the virtual Draw( ) method of theabstract class TShape. A variable S of type TShape can have, as itsvalue, an object of any of the derived classes of actual shapes,TRectangle, TSquare, TCircle and TOval. When the Draw( ) message is sentto the object that is the value of S, the individual customized Draw(

) method of the object that is the current value of S is executedand draws the individual shape in a fashion appropriately definedfor its particular derived class. Thus, for example, sending theDraw( ) message to each object on a list of individual shapesderived from the abstract class TShape causes the list of individualobject instances to be drawn each according to its own customizedDraw( ) method.

4. In C++ the keyword this is a variable whose value points to theobject to which the current method is being applied.

5. In C++, executing X = new TObject; allocates space in dynamic memoryfor an object instance of type TOb jec t , returns a pointer to thestorage for this object instance, and stores the pointer as thevalue of the variable X. Executing the statement delete X; deletesthe space for the object instance pointed to by the pointer valueof the variable X and returns the space to the pool of availabledynamic memory in the heap.

Solutions to Selected Exercises in Exercises 15.21. The solution is given by the function UserDesignatesShape on lines

22:69 of Program 15.21 (on pages 635:636).

2. The solution is given by the following program which designatesmoveable lists of hollow or filled shapes in which the filled shapescan be filled by LtGray, CrossHatch or HorizontalLines patterns:

| / / / / /| / /| / / Ex. 15.2.2 -- Extension of Third Stage Drawing Program to use| / / three distinct types of fill patterns for filled shapes

5 | / /| / / / / /|| #include <stdio.h>| #include <string.h>

10 | #include <oops.h>| #include "windowUtils.h"| #include "Shapes.h" // see Shapes.h and Shapes.c below| #include "ShapeList.h" // see ShapeList.h and ShapeList.c below|

15 || // Global variable that contains the shape list|| TList *theShapeList;|

20 | // Prompts user to give (deltaX, deltaY) move increments and

Page 233: Standish Sol Manual Ece223

Table of Contents — continued

| // calls Move method for theShapeList to move the entire list| // of shapes.|| void MoveShapes( )

25 | {| short deltaX, deltaY;|| if (theShapeList–>IsNonEmpty( )) {| fflush(stdin);

30 | printf("deltaX deltaY: ");| scanf("%hd%hd", &deltaX,&deltaY);| fflush(stdin);|| theShapeList–>Move(deltaX,deltaY);

35 | theShapeList–>Draw( );| }| }|| //////

40 | //| // UserDesignatesShape prompts the user for a shape type,| // creates a pointer to a shape instance of that type,| // and returns the shape pointer.| //

45 | //////|| static Boolean UserDesignatesShape (TShape **theShape)| {| char *reply = "           ";

50 | int ReplyLength;|| while (1) {|| printf("Designate one of: Q = Quit; M = Move shapes, HO = Hollow Oval,\n");

55 | printf("FO = Filled Oval, HR = Hollow Rectangle, FR = Filled Rectangle,\n");| printf("HRR = Hollow Rounded Rectangle, FRR = Filled Rounded Rectangle:

");|| fflush(stdin);| gets(reply);

60 || ReplyLength = strlen(reply);|| if ((reply[0] == 'Q') || (reply[0] == 'q')) {| return false;

65 || } else if ((reply[0] == 'M') || (reply[0] == 'm')) {| MoveShapes( );| *theShape = NULL;| return true; // stay in the loop

70 ||| } else {| switch(reply[1]) {| case 'O':

75 | case 'o': if ((reply[0] == 'H') || (reply[0] == 'h')) {| *theShape = new TOval;| } else {| *theShape = new TFilledOval;| (*theShape)–>GetItsPattern( );

80 | }| return true; // stay in the loop

Page 234: Standish Sol Manual Ece223

Table of Contents — continued

|| case 'R':| case 'r': if (ReplyLength < 3) {

85 | if ((reply[0] == 'H') || (reply[0] == 'h')) {| *theShape = new TRectangle;| } else {| *theShape = new TFilledRectangle;| (*theShape)–>GetItsPattern( );

90 | }| } else {| if ((reply[0] == 'H') || (reply[0] == 'h')) {| *theShape = new TRoundedRectangle;| } else {

95 | * t h e S h a p e = n e wTFilledRoundedRectangle;

| (*theShape)–>GetItsPattern( );| }| }| return true; // stay in the loop

100 | default: *theShape = NULL;| return true; // stay in the loop| } // end switch|| } // end if

105 || } // end while|| } // end UserDesignatesShape|

110 | //////| //| // The main( ) function initializes the system, prompts the user| // for instructions to create, draw, and move shapes and shapelists,| // and terminates the session when the user designates the "quit"

115 | // instruction code.| //| //////|| int main( void)

120 | {| TShape *theShape;| short x1, y1, x2, y2;|| SetUpWindows( );

125 | DrawCoordinateSystem( );|| theShapeList = new TList;| theShapeList–>InitShapeList( );|

130 | while (UserDesignatesShape(&theShape)) {|| if (theShape != NULL) {|| printf("left top right bottom: ");

135 | scanf("%hd%hd%hd%hd", &x1,&y1,&x2,&y2);| fflush(stdin);|| theShape–>SetEnclosingRect(x1, y1, x2, y2);| theShape–>Draw( );

140 | theShapeList–>Append(theShape);| }| }

Page 235: Standish Sol Manual Ece223

Table of Contents — continued

|| return(0);

145 | }|

The main program above for Ex 15.2.2 uses the Shapes and ShapeList

modules defined below. We first give the Shapes .h header filefollowed by the Shapes.c source file.

| //////| //| // The Shapes.h header file defines the Shape class hierarchy| //

5 | //////||| class TShape {

// the abstract class of which all|// actual Shape classes are derivatives

10 | public:|| // The SetEnclosingRect method sets (left, top, right, bottom)| // values for the EnclosingRectangle.|

15 | void SetEnclosingRect (short x1, short y1, short x2, short y2);|| void Move(int deltaX, int deltaY);|| void GetItsPattern(void);

20 || // The Draw( ) method draws the shape. All derived classes| // override it. At this abstract class level it is virtual,| // in order to behave as a place-holder that expects to be| // overridden by local customized methods in each derivative

25 | // subclass.|| virtual void Draw( );|| protected:

30 || Rect EnclosingRectangle; // The rectangle that encloses| // the shape| PatPtr itsPattern; // The fill pattern for the shape| // (if applicable)

35 || };|| class TRectangle : public TShape {|

40 | public:| void Draw( );| };|| class TOval : public TShape {

45 || public:| void Draw( );| };|

50 | class TRoundedRectangle : public TShape {

Page 236: Standish Sol Manual Ece223

Table of Contents — continued

|| public:| void Draw( );| };

55 || class TFilledRectangle : public TRectangle {|| public:| void Draw( );

60 | };|| class TFilledOval : public TOval {|| public:

65 | void Draw( );| };|| class TFilledRoundedRectangle : public TRoundedRectangle {|

70 | public:| void Draw( );| };|

The source file Shapes.c follows:

| //////| //| // Shapes.c contains the implementations of TShape methods| //

5 | //////|| #include <oops.h>| #include <stdio.h>| #include <string.h>

10 | #include "windowUtils.h"| #include "Shapes.h"|| extern WindowPtr DrawingWindow; // The drawing window is| // defined in windowUtils.c

15 || Pattern HorizontalLines = {0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00};| Pattern CrossHatch = {0x88, 0x11, 0x22, 0x44, 0x88, 0x11, 0x22, 0x44};||

20 | void TShape : : SetEnclosingRect (short x1, short y1, short x2, short y2)| {| EnclosingRectangle.top = 100 – y1;| EnclosingRectangle.left = 150 + x1;| EnclosingRectangle.bottom = 100 – y2;

25 | EnclosingRectangle.right = 150 + x2;| }|| void TShape : : Move(int deltaX, int deltaY)| {

30 | EnclosingRectangle.top –= deltaY;| EnclosingRectangle.left += deltaX;| EnclosingRectangle.bottom –= deltaY;| EnclosingRectangle.right += deltaX;| }

35 || void TShape : : GetItsPattern(void)

Page 237: Standish Sol Manual Ece223

Table of Contents — continued

| {| char *reply = "        ";| int ReplyLength;

40 || printf("Designate one of: G = LtGray, C = CrossHatch, \n");| printf("              H = Horizontal Lines : ");|| fflush(stdin);

45 | gets(reply);| ReplyLength = strlen(reply);|| switch (reply[0]) {| case 'G':

50 | itsPattern = &ltGray;| break;| case 'C':| itsPattern = &CrossHatch;| break;

55 | case 'H':| itsPattern = &HorizontalLines;| break;| default:| itsPattern = &ltGray;

60 | break;| }| }| // end of GetItsPattern|

65 | // The Draw( ) method is a virtual method that should be| // overridden by individual customized Draw( ) methods in| // each derivative Shape subclass|| void TShape : : Draw ( )

70 | {| SetPort(DrawingWindow);| }|| // The TRectangle : : Draw( ) method calls its parental Draw( ) function

75 | // to set the port to the drawing window and then draws a wire frame| // shape for a rectangle.|| void TRectangle : : Draw ( )| {

80 | TShape : : Draw( );| FrameRect(&EnclosingRectangle);| }|| // TOval : : Draw

85 || void TOval : : Draw ( )| {| TShape : : Draw( );| FrameOval(&EnclosingRectangle);

90 | }|| // TRoundedRectangle : : Draw|| void TRoundedRectangle : : Draw ( )

95 | {| TShape : : Draw( );| FrameRoundRect(&EnclosingRectangle,20,20);| }

Page 238: Standish Sol Manual Ece223

Table of Contents — continued

|100 | // TFilledRectangle : : Draw

|| void TFilledRectangle : : Draw ( )| {| FillRect(&EnclosingRectangle,*itsPattern);

105 | TRectangle : : Draw( );| }|| // TFilledOval : : Draw|

110 | void TFilledOval : : Draw ( )| {| FillOval(&EnclosingRectangle,*itsPattern);| TOval : : Draw( );| }

115 || // TFilledRoundedRectangle : : Draw|| void TFilledRoundedRectangle : : Draw ( )| {

120 | FillRoundRect(&EnclosingRectangle,20,20,*itsPattern);| TRoundedRectangle : : Draw( );| }|

The main program above for Ex 15.2.2 uses the ShapeList moduledefined below. The header file ShapeList.h is given first, followed bythe source file ShapeList.c.

| //////| //| // ShapeList.h| //

5 | //////||| // define the Shape List base class|

10 | class TList| {|| public:|

15 | void InitShapeList( );| void Append(TShape *theShape);| void Move(int deltaX, int deltaY);| void Draw( );| Boolean IsNonEmpty( );

20 || private:|| TShape *Shape;| TList *Link;

25 || };

The source file ShapeList.c follows:

| //////

Page 239: Standish Sol Manual Ece223

Table of Contents — continued

| //| // ShapeList.c| //

5 | //////|| #include <oops.h>| #include "Shapes.h"| #include "ShapeList.h"

10 || extern WindowPtr DrawingWindow; // The drawing window is| // defined in windowUtils.c|| void TList : : InitShapeList( )

15 | {| Shape = NULL; // Convention: A list node with NULL shape is| Link = NULL; // an empty node awaiting a non-null|//Shape to be assigned| }

20 || void TList : : Append (TShape *theShape)| {| TList *LastNode, *NewNode;|

25 | if (Shape == NULL) { //overwrite an "empty" shape with|// theShape to fill in the empty slot| Shape = theShape;|| } else {

30 || NewNode = new TList;| NewNode–>Shape = theShape;| NewNode–>Link = NULL;| LastNode = this;

35 | while (LastNode–>Link != NULL) {| LastNode = LastNode–>Link;| }| LastNode–>Link = NewNode;| }

40 | }|| void TList : : Move(int deltaX, int deltaY) //calls Move(deltaX, DeltaY)| { // method on each Shape in the list| TList *TempNode;

45 | TShape *theShape;|| TempNode = this;|| while (TempNode != NULL) {

50 || theShape = TempNode–>Shape;| theShape–>Move(deltaX, deltaY); //call theShape's move|// method to move it by (deltaX,deltaY)| TempNode = TempNode–>Link; //advance to next

55 | // node on list| }| }|| void TList : : Draw( )

60 | {

Page 240: Standish Sol Manual Ece223

Table of Contents — continued

| TList *TempNode;|| TempNode = this;| while (TempNode != NULL) {

65 || TempNode–>Shape–>Draw( ); //send the Draw message|// to the shape in the node's Shape instance variable| TempNode = TempNode–>Link; //advance to next node on list| }

70 | }|| Boolean TList : : IsNonEmpty( )| {| return (Shape != NULL); // a NULL Shape in the first list node

75 |// signifies an empty list, by convention| }|

Answers to Review Questions 15.31. In an object-oriented graphics drawing system, one kind of

objects can be a “grouped object” consisting of a pointer to alinked list of subobjects that are grouped together, having anenclosing rectangle that is the smallest rectangle containing all ofthe enclosing rectangles of the individual subobjects on thesubobject list. Such a grouped object can be drawn or moved bysending a “draw” or “move” message to each object on itssubobject list.

2. A user interface to a system is that part of the system whichinteracts with users by accepting user inputs (e.g., fromkeyboards, pointing devices, microphones, video cameras, etc.)and giving outputs to the user (e.g., on computer display screens,or with speakers in the case of sounds, etc.)

3. Object-oriented programming techniques can be used toadvantage to create file and printing subsystems for a softwareapplication by allowing programmers to customize general-purpose, prefabricated subsystem objects. For example, a printingsubsystem may have an abstract virtual method for drawing a“page image” which functions like a “blank method” for theprogrammer to fill in by supplying a specific “draw page image”method. This is the only missing piece needed to complete theprinting subsystem. Similarly, the programmer can fill in theblanks in a file subsystem object by supplying converter methodsthat translate byte streams in a file into and out of internal objectformat. Thus, the programmer need only specify the least amountof application-specific detail to complete a prefabricatedsubsystem design — a design which may have been very carefully

Page 241: Standish Sol Manual Ece223

Table of Contents — continued

conceived to handle all exception conditions and device typesrequired.

4. Object-oriented systems may provide building-block objects withwhich to construct user interfaces. Using a graphics-orientededitor, it may be possible to design a “control panel” withbuttons, sliders, and other input devices to perform button-pressactions, such as starting, stopping, and resetting, or to performquantity-specifying actions, such as that achieved by adjusting aslider indicator on a slider scale. Further, it may be possible toconnect the model’s outputs to the inputs of display objects thatcan display the model’s outputs using graphics, text, or sound.

Solutions to Selected Exercises in Exercises 15.31. Let L be a list of objects (O1, O2, ... , On), having respective

enclosing rectangles R1, R2, ... , Rn, where each rectangle Ri is astruct having four members of the form Ri = {lefti, topi, righti,bot tomi}. The smallest rectangle enclosing the Ri (1 ≤ i ≤ n ) isobtained by computing the rectangle R = {left, top, right, bottom}whose left and bottom are the respective minima of the lefti andbottomi, and whose top and right are the respective maxima ofthe topi and righti of the Ri for (1 ≤ i ≤ n).

2. To implement a method to ungroup a grouped object G, one needmerely append the subobjects on G’s own list of subobjects tothe end of theShapeList, the global list of currently visible shapes inthe drawing application, after which the space for the groupedobject G can be reclaimed (by calling delete G).

3. Let L = (O1, O2, ... , On) be a list of shape objects having respectiveenclosing rectangles R1, R2, ... , Rn, where Ri is of the form Ri ={left i, topi, righti, bottomi}, and let R = {left, top, r ight, bo t tom}be the smallest rectangle enclosing all the Ri (1 ≤ i ≤ n) computedaccording to the method given in Ex. 15.3.1 above. Now define thecenter of a rectangle R = {left, top, right, bottom} to be the pointC = {HorizontalCenter, Vertical Center} where HorizontalCenter =(left+right)/2 and VerticalCenter = (top+bottom)/2. To align theobjects on list L horizontal ly, so that their bottoms, centers, ortops align, you can move them so that the enclosing rectangle Riof object Oi has its respective bottom, center, or top lying on therespective bottom, center, or top of the enclosing rectangle R.

Similarly, to align the objects on list L vertically, so that theirlefts, centers, or rights align, you can move them so that theenclosing rectangle Ri of object Oi has its respective left, center,or right lying on the respective left, center, or right of theenclosing rectangle R. The align message to send to an object is

Page 242: Standish Sol Manual Ece223

Table of Contents — continued

just a variant of the Move(deltaX, deltaY) message defined on lines51:57 of Program 15.20 (on p. 634), where the distances to movean object deltaX and deltaY to produce the desired alignment areobtained by differencing the left, top, right, bottom, or center ofthe enclosing rectangle Ri of the object to move with theappropriate left, top, right, bottom, or center of the smallestrectangle R enclosing all of the objects Oi (1 ≤ i ≤ n)

Answers to Review Questions 15.41. Some advantages are: (a) Provides a framework in which reusable

software components can be expressed and used — and in a form inwhich objects are customizable to fit differing circumstances ofcomponent use; (b) Provides for ease of maintenance andmodification of software permitting programming to occur only by“expressing differences”; (c) Provides clear modular structure forsoftware at a level of organization finer than the C source module;(d) Provides for fast easy definitions of prototypes and userinterfaces; and (e) Enables prefabricated software subsystems,such as those for printing and filing, to be converted into finishedsubsystems merely by filling in the blanks with application-specific components.

2. If objects are allocated storage in heaps and are referenced byhandles (pointers to pointers) in order to make heap compactingefficient, there may be run-time penalties associated withstorage allocation and double dereferencing of handles. If dynamicbinding of method names to actual method implementations isrequired at run time, there may be penalties associated withperforming searches to determine these run-time bindings — as inthe case when a message for method M sent to object Ob1 does notbind to a local method for O b 1 , but instead binds to a methoddefined for a remote ancestor of Ob1 in the class inheritancehierarchy.

3. Class browsers help perform convenient, on-line searches duringprogram editing and composition to locate textual definitions ofclasses and methods, and they portray class hierarchiesgraphically, allowing menu-driven navigation of and access to thedefinitions in the class hierarchy. This is much more efficient thanattempting to search in cross-reference listings, since the lattertend not to be organized advantageously to support searches forclass definitions and methods, especially those sharing identicalmethod names.

Solutions to Exercises 15.4

Page 243: Standish Sol Manual Ece223

Table of Contents — continued

1. In order to draw the class hierarchy, you have to build a tree (or aforest) at compile time which encodes the subclass relationsbetween class definitions. Such a forest is shown in Fig. 15.32,using a horizontal graphical forest representation in which theroots of the trees in the forest lie to the left. You also have tosave the object class names to use as tree node names. Todisplay a menu of method names, such as that shown in Fig. 15.33,you have to attach to each tree node representing a classdefinition, the names of each of the methods defined for thatclass definition. Moreover, you have to save a textual referencefor each such method or for each such class declaration givingboth the text file path name and also the starting and ending linenumbers of the definition’s text in this text file, in order to beable to display the text of the respective method or classdeclaration.

Page 244: Standish Sol Manual Ece223

Table of Contents — continued

Answers to the Review Questions 16.21. The software lifecycle is the span of time that starts with the

initial conception of a software system and ends with itsretirement from use, following its useful service lifetime. It hasbeen found useful to break the software lifecycle into variousphases consisting of activities such as requirements analysis,s p e c i f i c a t i o n , d e s i g n , coding and debugging, testing andintegration, and operations and maintenance.

In requirements analysis, we try to formulate a high-qualitystatement of the true capabilities and properties of the systemthat are needed by its users and operators. The result of therequirements analysis process is usually a requirements documentthat enumerates the requirements the system will have to meet.In general, in a good requirements analysis, we’re trying toconceive of everything that is relevant and nothing more. Weattempt to generate a set of requirements statements with atleast the following properties: (a) completeness, (b) consistency,(c) unambiguity, (d) correctness, (e) comprehensibility.

Spec i f i ca t i on is the enumeration of specific, quantitativebehavioral constraints a system must satisfy in order to meet itsrequirements. Good specifications are: (1) c o m p l e t e , (2)unambiguous, (3) minimal, (4) comprehensible, and (5) sufficientlyspecific and well-quantified to be testable whether or not theyare satisfied. Specification is a key activity in the softwarelifecycle because, in the absence of specific measurableproperties a system must exhibit to meet its requirements, itmay not be testable whether a system satisfies the requirements.

A d e s i g n is a representation of an artifact or a system. Asoftware design is a representation of a software system suitablefor programmers to use to implement it. Designing is the art ofconstructing and evaluating designs to meet constraints andsatisfy purposes. In the software lifecycle, we try to producedesigns: (a) To satisfy the specifications and requirements, (b) Tohelp prescribe further implementation activities, and (c) Toexhibit certain useful properties, namely: (1) completeness, (2)consis tency, (3) comprehensib i l i ty , (4) technical feasibility, (5)unambiguity, and (6) susceptibility to analysis and evaluation.

During the coding and debugging phase of the software lifecycle(sometimes called the implementation phase) you construct anddebug a working executable representation of the system design.During implementation, we try not only to produce a concretedesign realization in a suitable programming language that

Page 245: Standish Sol Manual Ece223

Table of Contents — continued

satisfies the specifications and meets the requirements, we alsoattempt to produce an artifact that enhances the ease ofsubsequent maintenance and modification.

During module testing and integration, we attempt to ensurethat modules have correct behavior, that they satisfyperformance specifications, and that they cooperate correctlytogether to achieve overall system performance. Testing andintegration is a key phase of the software lifecycle because itensures that software quality requirements are met prior tosystem release.

During the maintenance and upgrade phase of the lifecycle, weattempt to alter the system either to remedy defectiveproperties revealed during system usage, or to meet newbehavioral requirements. The activi t ies involved duringmaintenance may recapitulate any and all of the previous lifecycleact iv i t ies.

2. In the software lifecycle, good designs: (a) Satisfy thespecifications and requirements, (b) Help prescribe furtherimplementation activit ies, and (c) Exhibit certain usefulproperties, namely: (1) c o m p l e t e n e s s , (2) c o n s i s t e n c y , (3)comprehensibi l i ty, (4) technical feasibility, (5) unambigui ty, and(6) susceptibility to analysis and evaluation.

3. After finishing a design, a useful practice is to conduct a des ignwalkthrough. In a design walkthrough, the designers try to explainthe design to a critical (but friendly) team of design reviewers(who should be distinct from the actual designers, themselves).The idea is to go over the design with a “fine-toothed comb,” so-to-speak, to attempt to catch errors and problems early in thelifecycle before investing in costly implementations that will bethrown away or that will need costly rework.

4. The maintenance and upgrade phase of the lifecycle oftencomprises 70% to 90% of the total lifecycle cost of a large, long-lived software system. In other words, maintenance is usuallyexpensive. This implies that spending extra time and effort in theprerelease phases of the software lifecycle, in order to decreasedownstream maintenance costs, can yield significant cost-avoidance. For example, by one estimate, only 17% of the timespent in the maintenance phase is attributable to debugging, butover 50% of the time is spent by programmers trying tounderstand the system they need to repair or extend. If softwareunde rs tand ing constitutes 50% or more of the maintenanceactivities, then a key prerelease, cost-avoidance activity isproducing clear documentation (or other types of system

Page 246: Standish Sol Manual Ece223

Table of Contents — continued

understanding aids, such as videotapes of key designers andimplementers explaining what was going through their minds whenthey built the system). Implementing the design in a clean,modular fashion yields implementations that are susceptible tomodification and upgrade at less expense than less modularimplementations. Also, maintenance costs can be reduced (oravoided) by ensuring that tried-and-proven techniques are usedfor trouble report handling, installing new system releases, andupdating both documentation and system configurations.

Solutions to Selected Exercises in Exercises 16.21. Some sensible general requirements are that the system should

be affordable, reliable, efficient, correct, and user-friendly. (Thislist is not exhaustive.) By affordable, you might mean that the costof system development and operation should live within thesystems development budget and the systems operation budgetagreed upon at the time the development contract is signed bythe client. (Such budgets reflect the amount the client is willing topay and expects to pay to purchase and operate the system.) Byreliable, you might mean that the system should be continuouslyavailable and should function correctly after it is released forservice and put into operational use. By efficient, you might meanthat the system as implemented does not waste resources anduses the minimum resources needed to satisfy all the othersystem requirements. By correct, you might mean that the systemoperates free of error. By user-friendly, you might mean that thesystem’s inputs and outputs are comprehensible, that users find iteasy to learn how to use the system, and that the system isresponsive.

2. Taking the r e s p o n s i v e n e s s subrequirement of the u s e r -friendliness requirement: (a) For on-line transactions at tellerwindows in branch banks, the system shall be deemed responsiveif, after submission of a command at a teller terminal, the systemresponds within 1/2 second for 95% of the transactions, within 2seconds for 99.8% of the transactions, and in no more than 5seconds for all transactions (where the “time of submission” ofthe command is defined as the time the teller presses the “enter”key on the keyboard); (b) For printing and mailing of monthlystatements to customers, the system shall be deemed responsiveif it never delays scheduled printing and mailing of a batch ofstatements more than two days after the scheduled mailing time.Taking the correctness requirement: (a) The system shall notmake data recording errors by entering a datum into its databasethat differs from that in an executed data entry command on the

Page 247: Standish Sol Manual Ece223

Table of Contents — continued

screen of any bank data entry terminal; (b) The system shall notlose or corrupt data in its database; (c) The system shall make nocalculation errors, nor shall it fail to make calculations that areprecise to the nearest 1/1000th of a cent.

Answers to the Review Questions 16.31. In general, productivity is a measure of the output achieved from

spending a unit’s worth of input effort. Software productivity canbe measured by dividing the number of lines of delivered sourceinstructions (DSI) by the number of person-months (PMs) neededto build a system prior to its release into service.

2. The software learning curve refers to an observed phenomenonthat when a team of system programmers implements a system(or a family of substantially similar systems) repeatedly underslightly changing circumstances, the resources consumed forsystem development declines dramatically on the second andsucceeding attempts as trial-and-error behavior on the first passgives way to the use of tried-and-true techniques on succeedingpasses. Not only does the team’s performance improve, but itsability to estimate its future resource consumption accuratelyimproves as well.

3. To conduct a software productivity audit you first rate yourorganization’s productivity attributes using COCOMO rating sheets.Then, among those attributes with low productivity ratings, youidentify those that can be addressed as manipulable cost-driversin which you could make investments to improve productivity.Next, you do a cost-benefit analysis which estimates the return oninvestment (ROI) in terms of quant i f ied product iv i tyimprovements attainable by making various productivity-improving investments. Finally, you identify those investments, ifany, that yield a payoff as potential candidates for performance-enhancing, net-cost reducing improvements to make.

4. The effort and schedule equations favor the use of reusablesoftware components when the sum of the costs of: (a) componentacquisition, (b) learning how to use the components, and (c)implementing the “glue code” to assemble the components into asystem, is less than the cost of implementing the system fromscratch starting with a clean sheet of paper.

Solutions to Selected Exercises in Exercises 16.31. Using Eq. 16.1, with KDSI = 100, m7 = 0.93, and m14 = 0.83, yields

the number of person months (PMs) to develop the system:

Page 248: Standish Sol Manual Ece223

Table of Contents — continued

PM = m7 * m14 * 2.4 * (KDSI)1.05

= 0.93 * 0.83 * 2.4 * 1001.05

= 233.2 person months

Then, substituting this value of PM in Eq. 16.2 to get Td yields,

Td = 2.5* √3

233.2

= 15.4 months

Hence, the nominal development time is 15.4 months.

2. It should be possible to compress the nominal schedule to 75% ofits value at an extra 23% cost. Taking 75% of 15.4 months yields11.54 months for the compressed schedule. It should cost anadditional 53.6 person months beyond the original 233.2 PMs toachieve this schedule compression. (At $100K per fully burdenedperson year, the added expense to meet the compressed scheduleshould be $447K.)

Answers to the Review Questions 16.41. A software process model is a scheme for organizing the

activities that take place during the software lifecycle whichdefines the different activities and stages of the softwaredevelopment process and which specifies the criteria fortransitioning from one stage to the next.

2. In the Code-and-Fix model, you repeatedly write code and debugit until enough substantially effor-free code exists to constitute asystem implementation. In the Waterfall Model, you transitionthrough distinct stages consisting of requirements analysis,specification, design, coding and debugging, testing andintegration, and maintenance. In the Evolutionary Developmentmodel, you expand an initial partial working solution in incrementsusing directions for expansion determined by experience with thepartially working system. In the Spiral Model, you perform anumber of iterations or passes with the activities in each passdetermined by a risk analysis of the alternative courses of actionneeded to meet the objectives. Risk analysis includes risk itemidentification, prioritization, and resolution.

3. Risk analysis in the Spiral Model follows the determination ofobject ives to be attained, a l ternat ives for implementation of aportion of the system, and the const ra in ts imposed when thealternatives are pursued. Risk analysis consists of evaluating thea l ternat ives with respect to the ob jec t ives and const ra in ts toidentify uncertainties that are significant sources of risk to theproject. Risk item identification is the process of determining and

Page 249: Standish Sol Manual Ece223

Table of Contents — continued

enumerating a list of these uncertainties that impose risk. R i s kitem prioritization consists of arranging the risk items in highest-to-lowest order of priority for subsequent attention and action.Risk item resolution consists of devising and following cost-effective strategies for eliminating the high-priority risk items.

4. The prototyping or simulation activities used in the EvolutionaryDevelopment model can be considered as risk resolutionstrategies because they are activities that buy information toreduce risk. For example, building and using a prototype userinterface with actual users can develop information that reducesthe risk that the user interface design will be found to beineffective for these actual users. (This is useful information tobuy when confronting the design of ill-understood parts of asystem, such as user interfaces.) Simulating system performancecan reduce the risk that the proposed hardware configuration iseither insufficient to meet the project performance constraints orso excessive as to be wasteful of resources.

Solutions to Selected Exercises in Exercises 16.41. Designing a user interface in a software project in a new

application area or using a novel interface technology (such as a3D virtual reality helmet to navigate a database) presents the riskthat designers cannot easily foresee how actual future users willreact to the user interface, and that the interface design will notbe well-received by future users. In the Spiral Model this risk ofpossible design failure can be addressed by constructing a rapidprototype of the new user interface and by evaluating it in trialuse with real users to see if it works as expected (or to learn howto adjust the design in case it doesn’t work effectively). In theWaterfall Model, the user-interface design gets implemented,debugged, and tested against the specifications before it gets putinto actual use with real users in the post-release period. Thus, alooming disaster, such as a poorly-designed user interface, maygo undetected until after the system is released into service withactual users. Only then do the actual users generate informationthat the user-interface design is ineffective.

2. The development of ARTS III did not follow the Waterfall Modelbecause actual end-users (i.e., terminal area radar air trafficcontrollers) got to use and evaluate early, partially-completedversions of the system, and the system design was revised andimproved in response to constructive suggestions these usersmade. In the Waterfall Model, end-users wouldn’t have been able touse the system until after it was finished and was released for

Page 250: Standish Sol Manual Ece223

Table of Contents — continued

service. Instead, the development of ARTS III followed theEvolutionary Development model because end-users reacted toaspects of an evolving design as it was progressing towardcompletion.

Page 251: Standish Sol Manual Ece223

Table of Contents — continued

Answers to Review Questions A.11. An arithmetic progression is an ordered sequence of terms in

which any two consecutive terms ti and ti+1 differ by a constantamount d = ti+1 – ti .

2. n(n + 1)/2.

3. S = n *(a + l)/2.

4. Write down two copies of S = a + (a + d) + (a + 2d) + . . . + (l – 2d) +( l – d ) + l, with the terms of the second copy of S written inreverse order. Then add the two copies of S together, getting 2*S,and solve for S.

5. top – bottom + 1.

Solutions to Exercises A.11. S = 1 + 2 + 3 + . . . + 99 = 99 * (99 + 1) / 2 = 50 * 99 = 4950 cc’s.

2. S = (n/2)[ 2 l – (n – 1)d ].

3. S = (l + a)(l – a + d)/(2 d).

4. S = (3/2)(n + 2)(n – 3).

5. The formula solving Exercise 3 above gives the sum S in terms ofthe first term a, the last term l, and the constant difference d. Inthe case of the current exercise, a  = 2, l = 2 n , and d = 2.Substituting these values in the formula of Ex. 3 and simplifyinggives S = (2 n + 2)(2 n – 2 + 2)/(2*2) = n ( n + 1 ).

6. S = (n – 2)(n + 4).

7. S = 202*n.

Answers to Review Questions A.21. A geometric progression is an ordered sequence of terms having a

constant ratio r between pairs of consecutive terms. Thus, r = ti /ti+1 for each pair of consecutive terms ti and ti+1.

2. Multiply the sum S = a + ar +. . . + arn by r, getting a formula for rS,subtract S from rS, and solve for S.

3. The sum S of 1/2 + 1/4 + . . . + 1/2n is given by S = 1 – 1/2n . Thereforeas n increases, 1/2n gets smaller and smaller, and S gets closer andcloser to 1 — but S never exceeds 1. Hence, 1 is an upper limit forS.

4. If T is a tree in which all internal nodes have b children, thebranching factor of T is b.

Page 252: Standish Sol Manual Ece223

Table of Contents — continued

Solutions to Exercises A.21. Applying formula A.8, in which S = (r n+1 – 1)/(r – 1) to S = 1 + 21 +

22 + . . . + 2n, where r = 2, gives S = 2n+1 – 1.

2. The total number of nodes in a complete binary tree with a fullbottom row on level l is 2l+1 – 1, as can be seen by substituting lfor n in the answer to Ex. 1 above.

3. 1/4 – 1/2n .

4. 1/2 j–1 – 1/2k .

5. 2 – (k+2)/2n

6. $1656.03 (which is the sum of 100*(1.09)1 + 100*(1.09)2 + . . . +100*(1.09)10).

7. S = a * [ (r n – k+2 – 1) / (r – 1) ].

Answers to Review Questions A.31. If x is a real number, the floor of x is the largest integer y, such

that y ≤ x, and the ceiling of x is the smallest integer y, such that y≥ x.

2. Minus the floor of x is the ceiling of minus x. In symbols, – x = –x .

3. Whenever x is an integer x = x = x .

4. Whenever x is a negative real number that is not an integer, thenthe floor of x is not equal to x with its fractional part discarded.For example, – 6.3 ≠ – 6. Instead, – 6.3 = – 7.

Solutions to Exercises A.31. 3, – 2, 3, and 1.

2. The conjecture is false. As a counterexample, let x = 3.7 and y =3.2. Then, even though 3.7 ≤ 3.2 is true, it is not true that 3.7 ≤3.2.

3. The assertion is true. Because x ± y is always an integer, say a,and because the ceiling of an integer a is the same integer a, theresult follows from substituting a = x ± y in a = a.

Answers to Review Questions A.4

Page 253: Standish Sol Manual Ece223

Table of Contents — continued

1. The base-2 logarithm of a positive real number x, denoted lg x, isthe power to which the base 2 must be raised to be equal to x. Insymbols, 2 lg x = x.

2. Apply formula A.22 for changing bases of logarithms: logb x = logcx / logc b.

3. logb (x y) = logb x + logb y.

Solutions to Exercises A.41. We have the facts that b > 2 and n > k. Starting with k < n, derive

the result using the following steps:

Step to get next Step

k < n divide both sides by n

k/n < 1 add 1 to both sides

1 + k/n < 2 combine with 2 < b (a startingfact, listed above)

1 + k/n < b multiply both sides by n

n + k < n* b take base-b logarithms ofboth sides

logb (n+k) < logb (n*b) expand logb (n*b) and replace logb b by1.

logb (n+k) < logb n + 1.

[Note : The latter result can be used to show that logb(n + k) isO(log n).]

2. lg 4x2 = lg (2x)2 = 2 lg(2x) = 2 [ lg 2 + lg x ] = 2 (lg x + 1).

3. Apply formula A.24 to show that ln((8x)/y) / ln 2 = lg ((8x)/y). Thenexpand lg ((8x)/y) to lg 8 + lg x – lg y which equals 3 + lg x – y.

Answers to Review Questions A.51. If a and b are any two real numbers, then if b ≠ 0, we define a mod b

= a – b a/b . Otherwise, if b = 0, we define a mod b = a.

2. a ≡ b (modulo m) means (a mod m) = (b mod m).

3. The modulus is m.

4. You can cancel c whenever c and m are relatively prime.

Page 254: Standish Sol Manual Ece223

Table of Contents — continued

Solutions to Exercises A.51. 3, 4, and 0.18.

2. The following table demonstrates the result.

i = 0 1 2 3 4 5 6 7 8 9 10

i * 5 = 0 5 10 15 20 25 30 35 40 45 50

(i*5) mod 11 = 0 5 10 4 9 3 8 2 7 1 6

Answers to Review Questions A.61. The general term is F(i) and the index variable is i.

2. An index variable is sometimes called a dummy variable becauseit acts as a “place holder” for a range of values, analogous to therole of a “dummy corporation” which is set up to act in place ofanother corporation — as a place holder — to represent the othercorporation’s interests.

3. The nth Harmonic number, Hn, is the sum of the reciprocals of theintegers ranging from 1 to n. Thus, Hn = ∑(1≤ i≤n) 1/i = 1 + 1/2 + 1/3 +. . . + 1/n.

4. You can replace the index variable in a sum with an expressioninvolving another index variable provided the substitution yieldsa permutation of the range.

Solutions to Exercises A.6

1. Putting d = 1/2 in ∑(1≤ i≤n) i d i =

d

(d–1)2 [ (nd – n – 1) dn + 1] yields,

1 /2

(–1/2)2 [ –

 n + 2  

2   1  

2n + 1] , which simplifies first to,

2 [ 1 –  1  2

 n + 2  

2n ] , and finally simplifies to,

2 –  n+2  

2n .

Equation A.28 follows from the latter by replacing n with k.

2. From Ex. A.1.3, when we know a, l, and d, but not n in an arithmeticprogression, the sum is given by S = (l + a )( l – a + d )/(2 d ) .Substituting a = 1, l = 2n – 1, and d  = 2 in the latter yields S = (2n – 1 + 1)(2n – 1 – 1 + 2)/(2*2) which simplifies to S = n2.

Page 255: Standish Sol Manual Ece223

Table of Contents — continued

3. Writing S = 2 + 4 + 6 + . . . + 2n as a sum with an index variable iyields S  = ∑ (1≤ i≤n ) 2*i. Because the range (1 ≤ i ≤ n ) containsexactly n numbers, there are n terms in the sum S . Therefore,applying formula A.1 (or Memory Jog A.1), the sum is the numberof terms n times the average of the first and last terms(2 +  2n )/2. Hence, S = n *(2 + 2n )/2 = n *(n +1). [N o t e : In thissolution, we used summation notation to deduce the number ofterms, n. Compare this solution with the solution to Ex. A.1.5, whichsolves the same problem a different way without deducing thenumber of terms.]

4. Solving the simultaneous equations,

1 = A + B + C + D

4 = 8 A + 4 B + 2 C + D

10 = 27 A + 9 B + 3 C + D

20 = 64 A + 16 B + 4 C + D

yields A = 1/6, B = 1/2, C = 1/3, and D = 0. Hence, S = n3/6 + n2/2 +n/3, which simplifies to S = n(n+1)(n+2)/6.

5. Yes, S = ∑(m ≤ i≤n) 1/2i = 2*(1/2m ) – (1/2n ) which is “twice the firstminus the last,” as can be seen by replacing j and k with m and nrespectively in the solution to Ex. A.2.4, giving S = 1/2m–1 – 1/2n,after which 1/2m–1 can be replaced by 2*(1/2m).

6. After replacing i with (j – 1), the sum ∑(–1 ≤ i ≤ n–1) 2i+1 becomes∑(0≤ j ≤ n) 2j which has the value 2n+1 – 1 (from formula A.8).

Answers to Review Questions A.7

1. The base case is T(0) = a. The general case is T(k) = 2 + 12 T(k/2).

2. A summing factor is a multiplier used to multiply severalinstances of recurrence relations so that when they are addedtogether they form a telescoping sum.

3. A telescoping sum is a sum of terms such that the terms in themiddle cancel one another in adjacent pairs.

4. Substitute the general formula for the solution into the originalrecurrence relations and verify that the relations hold.

Solutions to Exercises A.71. First, putting n = 1 in T(n) = b n + (a – b), we get T(1) = b*1 + (a – b)

= b + a – b so that T(1) = a holds in Eqs. A.35. For the general case,T(n–1) = b(n–1) + a – b. Substituting this for T(n–1) in b + c T(n–1),with c = 1 gives:

Page 256: Standish Sol Manual Ece223

Table of Contents — continued

= b + [ b (n–1) + a – b ]

= b + [ b n + a – 2 b ]

= b n + ( a – b )

= T(n).

Hence, the general case in recurrence relations A.35 is alsosatisfied by the solution T(n) = b n + (a – b).

2. If T(0) = a and T(n) = a + r T(n – 1), then by expansion,

T(0) = a

T(1) = a + a r

T(2) = a + a r + a r2

. . .

T(n) = a + a r + a r2 + . . . + a r n.

Using Eq. A.9, T(n) has the general form T(n) = a [(r n+1 – 1)/(r – 1)].

3. Let T(1) = a and T(n) = n d + (a – d) + T(n – 1). Rearranging T(n) tobe

T(n) = a + (n – 1)d + T(n – 1) and expanding gives,

T(1) = a

T(2) = (a + d) + T(2 – 1) = (a + d) + a

T(3) = (a + 2d) + T(2) = (a + 2d) + (a + d) + a

. . .

T(n) = (a + (n–1)d) + (a + (n–2)d) + . . . + (a + 2d)+ (a + d) + a

Hence T(n) is the sum of an arithmetic progression of n terms withfirst term a and last term l = a + (n–1)d. The general formula for

the sum can be found by applying Eq. A.3 to yield T(n) = n2 [ 2 a +

(n–1)d].

4. Letting a = 3, b = 1, and c = 2 in the general solution for T(n) inTable A.12 and simplifying gives, T(n) = 2n+1 – 1.

5. Rewrite Recurrence Relations A.35 as,

T(1) = a

T(n) – c T(n–1) = b

Then write,

T(1) = a

1 /c T(2) – c/ c T(1) = b /c {multiply by 1/c}

Page 257: Standish Sol Manual Ece223

Table of Contents — continued

1 /c2 T(3) – c/ c2 T(2) = b /c2 {multiply by 1/c2}. . .

1/cn–1 T(n) –c/ cn – 1 T(n–1) = b/cn–1 { m u l t i p l y b y1/cn–1}

{and summing the rowsabove}

T(n)/cn–1 = a + b/c + b/c2 + . . . + b/cn–1

In the sum immediately above, if c = 1 then T(n) = a + (n – 1) b = bn + (a – b), which yields the solution given in the first case of TableA.12. Now suppose c ≠ 1. Then the sum above is,

T(n)/cn–1 = a + b/c [ 1 + 1/c + 1/c2 + . . . + 1/cn–2 ]

= a + b/c [ (1 – 1/cn–1) / (1 – 1/c)]

= a + b [ (1 – 1/cn–1) / (c – 1)]

Multiplying both sides by cn–1 gives,

T(n) = a*cn–1 + b [ (cn–1 – 1) / (c – 1)]

= a*cn–1 + ( b*cn–1)/(c – 1) – b/(c – 1)

= [ a/c + b/(c(c – 1))] cn – b/(c – 1)

After some final simplification, this yields the solution for thecase c ≠ 1.

T(n) =

a  – b

c     +    b

c   –   1 cn – b

c   –   1 .

Answers to Review Questions A.81. Subexpressions having the logical values 0 (representing false)

and 1 (representing true) can be represented by propositionalvariables.

2. First, replace distinct logical subexpressions with distinctpropositional variables and replace the C’s logical operators &&, | |,and ! with ∧ , ∨ , and ∼ respectively. Second, use the laws in TablesA.13 and A.14 to simplify the latter logical expressions. Finally,reverse the substitutions of the first step to obtain simplified Cexpressions.

Solutions to Exercises A.81. The manager has the correct solution. The programmer has

misapplied the law a   ∨ t rue ≡ t rue of Table A.13 to concludeincorrectly that (!A) | | true simplified to (!A) instead of to true.

2. The input expression simplifies to (x == y).

Page 258: Standish Sol Manual Ece223

Table of Contents — continued

3. Use the first Ground Resolution law in Table A.14 to simplify theinput expression to (x–>top <= 9).

4. In C it is possible to use short circuit logical operators to guardagainst erroneous evaluation of subexpressions, as in (x == 0.0) | | (y/x

> 15). Here, x == 0.0 is true, the value of the entire subexpressionbecomes true and the subexpressions (y/x > 15) is never evaluated. Onthe other hand, if (y/x > 15) | | (x == 0.0) is evaluated with x == 0.0, a nerror occurs when evaluating y/x upon the attempt to divide y byzero. Thus, there exist circumstances in C in which the short circuitoperators & & and | | do not obey the commutative laws of logic.Caution should be exercised not to apply these commutative lawswhen attempting to simplify such logical subexpressions in C.