Sie sind auf Seite 1von 221

PD

PD

F-

XC

h a n g e Vi e

F-

XC

h a n g e Vi e

er

er

O W

bu

to

lic

lic

to

bu

N
.c

O W
w
.d o
c u -tr a c k

.d o

c u -tr a c k

.c

Handout #17 April 15, 2002

CS106B Section Handout 2

Spring 2002 Robert Plummer

Written by Nicholas Fang, with thanks to Jerry Cain, Bob Plummer, Eric Roberts, and the TAs that have come before.

Problem 1: Bracket Matching In the syntax of most programming languages, there are some characters that occur only in nested pairs, which are called bracketing operators. ANSI C, for example, defines the following bracketing operators:
( . . . ) [ . . . ] { . . . }

In a properly formed program, these characters will be properly nested and matched. To determine whether this condition holds for a particular program, you can ignore all the other characters and look simply at the pattern formed by the parentheses, brackets, and braces. In a legal configuration, all the operators match up correctly, as shown in the following example:

{ ( [ ] ) ( [ ( ) ] ) }

The following configurations, however, are illegal for the reasons stated:
( ( [ ] ) ) ( { ( } )

The line is missing a close parenthesis. The close parenthesis comes before the open parenthesis. The parentheses and braces are improperly nested.

For this problem, your task is to write a recursive function


bool IsBalanced(string str)

that takes a string str from which all characters except the bracketing operators have been removed. The function should return TRUE if the bracketing operators in str are balanced, which means that they are correctly nested and aligned. If the string is not balanced, IsBalanced should return FALSE. Although there are many other ways to implement this operation, you should code your solution so that it embodies the recursive insight that a string consisting only of bracketing characters is balanced if and only if one of the following conditions holds: 1. The string is empty. 2. The string contains "()", "[]", or "{}" as a substring and is balanced if you remove that substring.

w w w
w

w
w

PD

PD

F-

XC

h a n g e Vi e

F-

XC

h a n g e Vi e

er

er

O W

bu

lic

lic

2
w
.c

to

to

bu

N
w

O W
.d o
c u -tr a c k

.d o

c u -tr a c k

.c

For example, the string "[(){}]" is shown to be balanced by the following chain of calls:
IsBalanced("[(){}]") IsBalanced("[{}]") IsBalanced("[]") IsBalanced("") TRUE

Your implementation of IsBalanced must operate recursively and may include no explicit for or while statements. Problem 2: Arithmetic Combinations Write a function ArithmeticCombinations which given an array of integers and a desired result, recursively determines how many different ways you can combine those integers to form that result. Combining the numbers means choosing a sequence of arithmetic operations to apply to the numbers to produce an expression that evaluates to result. The sum starts as zero and you process the numbers in the array from left to right. For each number, you can either add it to the current sum, subtract it from the current sum or multiply the current sum by the number. All numbers in the array must be used in the expression. For example, consider the array ( 2 3 5 6) and desired result 15. There are two possible arithmetic combinations: 5 + 2 * 3 - 6 = 15 5 - 2 * 3 + 6 = 15 Note the expression is not evaluated using C precedence rules, it is simply evaluated left to right. The first two parameters are the array of numbers and its effective size (you can assume the array contains at least one number). The third parameter is the desired result. Your function should return the count of the different ways you can combine the numbers to get that result (you do not have to keep track of the combinations, just report the number of possible combinations). You will need to write a helper function. Use the following prototype:
int ArithmeticCombinations(int arr[], int n, int desiredResult)

w w w
w

PD

PD

F-

XC

h a n g e Vi e

F-

XC

h a n g e Vi e

er

er

O W

bu

lic

lic

3
w
.c

to

to

bu

N
w

O W
.d o
c u -tr a c k

.d o

c u -tr a c k

.c

Problem 3: Finding a Path Consider the following map, which shows six cities, labeled 0 through 5, and the roads that connect them. 0 1

The roads are all two-way, meaning that you can go from 0 to 1 and also from 1 to 0. This problem is concerned with finding pathways between cities. For example, if you want to know how to get from 0 to 5, you can look at the map and see that the way to do it is to go from 0 to 3 to 2 to 5. In this map you can get from any city to any other city, but not all maps are like that. For example, if a flood washed out the road from 0 to 3, then there would be no path from 0 to 5. Your job in this problem is to write a function that finds paths between cities in maps like this one (or determines that no path exists). The first thing to think about, of course, is how the map will be represented. We will do that using a "connection matrix", which is just a two-dimensional array of bools such that the element at position [i][j] is TRUE if there is a road between cities i and j, and FALSE if there isn't. Here is the matrix for the map shown above. The city numbers outside the box aren't part of the matrix; they are just there to help you relate the matrix to the map above. Also, weve just used T and F for TRUE and FALSE: 0 0 1 2 3 4 5 F T F T F F 1 T F F F T F 2 F F F T F T 3 T F T F F F 4 F T F F F F 5 F F T F F F

The fact that the roads are two-way means that the matrix is symmetric. That is, if element [i][j] is TRUE then element [j][i] is TRUE also. Just as a final check that you are with us on this, the fact that there is a road between cities 3 and 2 means that (using connect as the name of the array) both connect[3][2] and connect[2][3] are TRUE. Notice also that we have placed the value FALSE on the main diagonal. This will prove to be convenient, and you can assume that we will never ask our path finding function whether you can reach a city from itself.

w w w
w

PD

PD

F-

XC

h a n g e Vi e

F-

XC

h a n g e Vi e

er

er

O W

bu

lic

lic

4
w
.c

to

to

bu

N
w

O W
.d o
c u -tr a c k

.d o

c u -tr a c k

.c

Searching for paths between two specified cities can involve a lot of work, especially if the matrix is large. You might go quite a way down a certain path, only to find that it is a dead end as far as getting to your intended destination is concerned. In the map above, for example, if you were trying to find a path from 0 to 5, you might try going from 0 to 1 and then to 4. That doesn't get you there, so you will have to back up and try some more paths before finding what you are looking for or concluding that the path doesn't exist. Does this sound a bit familiar? That's right: one way to look for paths is with a recursive backtracking procedure, and that's what we want you to do. Your job is to write a recursive procedure PathExists with the following prototype (assume that NUM_CITIES is a #defined constant that gives the number of cities in the connection array):
bool PathExists(int from, int to, bool connect[][NUM_CITIES]);

Your function should return TRUE is a path exists between cities from and to, and FALSE if there is no path. Note that we haven't passed in the number of cities; you can just use the constant NUM_CITIES in your function as needed.

Problem 4: Recursion and Big-O Assume that the function F and its helper function G have been defined as follows:
int F(int n) { return G(n * n * n, n * n, n); } int G(int n1, int n2, int n3) { if (n1 != 0) return 1 + G(n1 - 1, n2, n3); else if (n2 != 0) return 1 + G(n1, n2 - 1, n3); else if (n3 != 0) return 1 + G(n1, n2, n3 - 1); else return 0; }

What is the value of F(3)? What is the computational complexity of the F function expressed in terms of N, where N is the value of the argument to F?

w w w
w

PD

PD

F-

XC

h a n g e Vi e

F-

XC

h a n g e Vi e

er

er

O W

bu

to

.d o

c u -tr a c k

.c

Handout #17S April 15, 2002

CS106B Section Solutions 2

Spring 2002 Robert Plummer

lic

lic

to

bu

N
w

O W
.d o
c u -tr a c k

.c

Thanks again to Jerry Cain, Bob Plummer, and Eric Roberts!

Problem 1: Bracket Matching


/* * Function: IsBalanced * Usage: if (IsBalanced(line)) { ... * ---------------------------------* This function determines if the given string, which is just * a series of open and close brackets with all other characters * removed, represents a properly balanced set of brackets. * This is done by working from inside out. If the string is empty, * then it is balanced by default. Otherwise, look for a consecutive * matched pair of brackets (either (), [], or {}). If one isnt * found, then this isnt a balanced string. Otherwise, remove the * pair and check the rest of the string to see if it is balanced. */ bool IsBalanced(string str) { string head, tail; int cp; if (StringEqual(str, "")) return (TRUE); cp = FindString("()", str, 0); if (cp == -1) cp = FindString("[]", str, 0); if (cp == -1) cp = FindString("{}", str, 0); if (cp == -1) return (FALSE); head = SubString(str, 0, cp - 1); tail = SubString(str, cp + 2, StringLength(str) - 1); return (IsBalanced(Concat(head, tail))); }

w w w
w

w
w

PD

PD

F-

XC

h a n g e Vi e

F-

XC

h a n g e Vi e

er

er

O W

bu

lic

lic

2
w
.c

to

to

bu

N
w

O W
.d o
c u -tr a c k

.d o

c u -tr a c k

.c

Problem 2: Arithmetic Combinations


/* * Function: ArithmeticCombinations * Usage: ArithmeticCombinations(values, numValues, target); * --------------------------------------------------------* A wrapper function for Combos, which figures out if it is possible * to combine the given values arithmetically in order to hit the * desired target value. */ int ArithmeticCombinations(int array[], int n, int desiredResult) { return Combos(array, n, desiredResult, 1, array[0]); } /* * Function: Combos * Usage: Combos(array, n, desiredResult, 1, array[0]); * ---------------------------------------------------* Recursively determines whether the values in the given array can be * combined using addition, subtraction, and multiplication to reach the * given target number. Each call is responsible for handling one number, * with the current number being kept track of by pos. The total number * of ways to reach the target is a combination of the ways to reach it * where the current target is added, subtracted, or multiplied to the * current value. When all numbers are done, if the total matches * the desired, then one way has been found. */ int Combos(int array[], int n, int desired, int pos, int total) { if (pos == n) return (total == desired ? 1 : 0); return (Combos(array, n, desired, pos + 1, total + array[pos]) + Combos(array, n, desired, pos + 1, total array[pos]) + Combos(array, n, desired, pos + 1, total * array[pos])); }

w w w
w

PD

PD

F-

XC

h a n g e Vi e

F-

XC

h a n g e Vi e

er

er

O W

bu

lic

lic

3
w
.c

to

to

bu

N
w

O W
.d o
c u -tr a c k

.d o

c u -tr a c k

.c

Problem 3: Finding a Path


/* * Function: PathExists * Usage: if (PathExists(from, to, connect)) { ... * ----------------------------------------------* This is a wrapper function for Path, which determines if there is * a path between two given cities in a map. Sets up a list to keep track * of which locations have been visited and clears this list, then calls * Path to actually do the work. */ bool PathExists(int from, int to, bool connect[][NUM_CITIES]) { int i; bool visited[NUM_CITIES]; for (i = 0; i < NUM_CITIES; i++) visited[i] = FALSE; return (Path(from, to, connect, visited)); } /* * Function: Path * Usage: return (Path(from, to, connect, visited)); * ------------------------------------------------* Recursively attempts to find a path from the from city to the to * city, where the connections between cities are specified by the * connection graph stored in connect. Does a standard recursive * backtracking algorithm, marking visited cities in the visited array * to prevent infinite loops. */ bool Path(int from, int to, bool connect[][NUM_CITIES], bool visited[]) { int i; if (visited[from]) return FALSE; if (from == to) return TRUE; /* note: if (connect[from][to]) is also correct here */ visited[from] = TRUE; for (i = 0; i < NUM_CITIES; i++) { if (connect[from][i] && Path(i, to, connect, visited)) return TRUE; } visited[from] = FALSE; return FALSE; }

Problem 4: Recursion and Big-O F(n) = n3 + n2 + n; so F(3) = 27 + 9 + 3 = 39. Complexity: F(n) is O (N3 + N2 + N), which is O(N3).

w w w
w

PD

PD

F-

XC

h a n g e Vi e

F-

XC

h a n g e Vi e

er

er

O W

bu

to

lic

lic

to

bu

N
.c

O W
w
.d o
c u -tr a c k

.d o

c u -tr a c k

.c

Eric Roberts CS106B

Handout #17 January 23, 2012

Section Handout #2ADTs


Problem 1. Using grids (Chapter 5, exercise 10, page 253) In the game of Minesweeper, a player searches for hidden mines on a rectangular grid that mightfor a very small boardlook like this:

One way to represent that grid in C++ is to use a grid of Boolean values marking mine locations, where true indicates the location of a mine. In Boolean form, this sample grid therefore looks like this:

Given such a grid of mine locations, write a function


void fixCounts(Grid<bool> & mines, Grid<int> & counts);

that creates a grid of integers storing the number of mines in each neighborhood. The neighborhood of a location includes the location itself and the eight adjacent locations, but only if they are inside the boundaries of the grid. The reference parameter counts is used to store the result. Your job in this exercise is to make sure that it has the same size as the mines grid and then to assign to each element an integer between 0 and 9. For example, if mineLocations contains the Boolean grid shown earlier, the code
Grid<int> mineCounts; fixCounts(mineLocations, mineCounts);

should initialize mineCounts as follows:

w w w
w

w
w

PD

PD

F-

XC

h a n g e Vi e

F-

XC

h a n g e Vi e

er

er

O W

bu

lic

lic

2
w
.c

to

to

bu

N
w

O W
.d o
c u -tr a c k

.d o

c u -tr a c k

.c

Problem 2. Using queues (Chapter 5, exercise 13, page 255) Bob Dylans 1963 song The Times They Are A-Changin contains the following lines, which are themselves paraphrased from Matthew 19:30: And the first one now Will later be last For the times they are a-changin In keeping with this revolutionary sentiment, write a function
void reverseQueue(Queue<string> & queue);

that reverses the elements in the queue. Remember that you have no access to the internal representation of the queue and will need to come up with an algorithm, presumably involving other data structures, to accomplish the task. Problem 3. Using maps (Chapter 5, exercise 21, page 259) In May of 1844, Samuel F. B. Morse sent the message What hath God wrought! by telegraph from Washington to Baltimore, heralding the beginning of the age of electronic communication. To make it possible to communicate information using only the presence or absence of a single tone, Morse designed a coding system in which letters and other symbols are represented as coded sequences of short and long tones, traditionally called dots and dashes. In Morse code, the 26 letters of the alphabet are represented by the following codes: A B C D E F G H I J K L M N O P Q R S T U V W X Y Z

Write a program that reads in lines from the user and translates each line either to or from Morse code depending on the first character of the line: If the line starts with a letter, you want to translate it to Morse code. Any characters other than the 26 letters should simply be ignored.

w w w
w

PD

PD

F-

XC

h a n g e Vi e

F-

XC

h a n g e Vi e

er

er

O W

bu

lic

lic

3
w
.c

to

to

bu

N
w

O W
.d o
c u -tr a c k

.d o

c u -tr a c k

.c

If the line starts with a period (dot) or a hyphen (dash), it should be read as a series of Morse code characters that you need to translate back to letters. Each sequence of dots and dashes is separated by spaces, but any other characters should be ignored.

The program should end when the user enters a blank line. A sample run of this program (taken from the messages between the Titanic and the Carpathia in 1912) might look like this (note that there are no spaces in the Morse-to-letters translation):
MorseCode

Morse code translator > SOS TITANIC ... --- ... - .. - .- -. .. -.-. > WE ARE SINKING FAST .-- . .- .-. . ... .. -. -.- .. -. --. ..-. .- ... > .... . .- -.. .. -. --. ..-. --- .-. -.-- --- ..HEADINGFORYOU >

Although it is easy to use a switch statement to convert from letters to Morse code, converting in the opposite direction requires a map. Given that you need to initialize a map for at least one of the directions, you should try to find a strategy that allows you to use the data in that map for both the letters-to-Morse and the Morse-to-letters translation. Problem 4. Using lexicons (Chapter 5, exercise 22, page 260) Section 3.6 defines the function isPalindrome that checks whether a word reads identically forward and backward. Use that function together with the English lexicon to print out a list of all words that are palindromes.

w w w
w

PD

PD

F-

XC

h a n g e Vi e

F-

XC

h a n g e Vi e

er

er

O W

bu

to

lic

lic

to

bu

N
.c

O W
w
.d o
c u -tr a c k

.d o

c u -tr a c k

.c

Eric Roberts CS106B

Handout #17A January 23, 2012

Solutions to Section Handout #2


Problem 1. Using grids

w w w
w

w
w

PD

PD

F-

XC

h a n g e Vi e

F-

XC

h a n g e Vi e

er

er

O W

bu

lic

lic

2
w
.c

to

to

bu

N
w

O W
.d o
c u -tr a c k

.d o

c u -tr a c k

.c

Problem 2. Using queues

w w w
w

PD

PD

F-

XC

h a n g e Vi e

F-

XC

h a n g e Vi e

er

er

O W

bu

lic

lic

3
w
.c

to

to

bu

N
w

O W
.d o
c u -tr a c k

.d o

c u -tr a c k

.c

Problem 3. Using maps

w w w
w

PD

PD

F-

XC

h a n g e Vi e

F-

XC

h a n g e Vi e

er

er

O W

bu

lic

lic

4
w
.c

to

to

bu

N
w

O W
.d o
c u -tr a c k

.d o

c u -tr a c k

.c

w w w
w

PD

PD

F-

XC

h a n g e Vi e

F-

XC

h a n g e Vi e

er

er

O W

bu

lic

lic

5
w
.c

to

to

bu

N
w

O W
.d o
c u -tr a c k

.d o

c u -tr a c k

.c

w w w
w

PD

PD

F-

XC

h a n g e Vi e

F-

XC

h a n g e Vi e

er

er

O W

bu

lic

lic

6
w
.c

to

to

bu

N
w

O W
.d o
c u -tr a c k

.d o

c u -tr a c k

.c

Problem 4. Using lexicons

w w w
w

PD

PD

F-

XC

h a n g e Vi e

F-

XC

h a n g e Vi e

er

er

O W

bu

to

lic

lic

to

bu

N
.c

O W
w
.d o
c u -tr a c k

.d o

c u -tr a c k

.c

CS106B Spring 03-04

Handout #11 April 12, 04

Section Handout #2
Problem 1: Cannonballs Suppose that you have somehow been transported back to 1777 and the Revolutionary War. You have been assigned a dangerous reconnaissance mission: evaluate the amount of ammunition available to the British for use with their large cannon which has been shelling the Revolutionary forces. Fortunately for you, the Britishbeing neat and orderlyhave stacked the cannonballs into a single pyramid-shaped stack with one cannonball at the top, sitting on top of a square composed of four cannonballs, sitting on top of a square composed of nine cannonballs, and so forth. Unfortunately, however, the Redcoats are also vigilant, and you only have time to count the number of layers in the pyramid before you are able to escape back to your own troops. To make matters worse, computers will not be invented for at least 150 years, but you should not let that detail get in your way. Your mission is to write a recursive function Cannonball that takes as its argument the height of the pyramid and returns the number of cannonballs therein.
static int Cannonball(int height);

Problem 2: ReverseString Given a string, create a function ReverseString that returns the string in reverse order. Consider both recursive and iterative techniques for solving this problem. Which one is easier to come up with?
static string ReverseString(string str);

Problem 3: GCD The greatest common divisor (g.c.d.) of two nonnegative integers is the largest integer that divides evenly into both. In the third century B.C., the Greek mathematician Euclid discovered that the greatest common divisor of x and y can always be computed as follows: If x is evenly divisible by y, then y is the greatest common divisor. Otherwise, the greatest common divisor of x and y is always equal to the greatest common divisor of y and the remainder of x divided by y. Use Euclid's insight to write a recursive function static the greatest common divisor of x and y.
int GCD(int x, int y)

that computes

w w w
w

w
w

PD

PD

F-

XC

h a n g e Vi e

F-

XC

h a n g e Vi e

er

er

O W

bu

lic

lic

2
w
.c

to

to

bu

N
w

O W
.d o
c u -tr a c k

.d o

c u -tr a c k

.c

Problem 4: Old-Fashioned Measuring (Chapter 5, Programming Exercise 10) I am the only child of parents who weighed, measured, and priced everything; for whom what could not be weighed, measured, and priced had no existence. Charles Dickens, Little Dorrit, 1857 In Dickenss time, merchants measured many commodities using weights and a two-pan balance a practice that continues in many parts of the world today. If you are using a limited set of weights, however, you can only measure certain quantities accurately. For example, suppose that you have only two weights: a 1-ounce weight and a 3-ounce weight. With these you can easily measure out 4 ounces, as shown:

It is somewhat more interesting to discover that you can also measure out 2 ounces by shifting the 1-ounce weight to the other side, as follows:

Write a recursive function


bool IsMeasurable(int target, int weights[], int nWeights)

that determines whether it is possible to measure out the desired target amount with a given set of weights. The available weights are stored in the array weights, which has nWeights as its effective size. For instance, the sample set of two weights illustrated above could be represented using the following pair of variables:
int sampleWeights[] = { 1, 3 }; int nSampleWeights = 2;

w w w
w

PD

PD

F-

XC

h a n g e Vi e

F-

XC

h a n g e Vi e

er

er

O W

bu

lic

lic

3
w
.c

to

to

bu

N
w

O W
.d o
c u -tr a c k

.d o

c u -tr a c k

.c

Given these values, the function call


IsMeasurable(2, sampleWeights, nSampleWeights)

should return true because it is possible to measure out 2 ounces using the sample weight set as illustrated in the preceding diagram. On the other hand, calling
IsMeasurable(5, sampleWeights, nSampleWeights)

should return false because it is impossible to use the 1- and 3-ounce weights to add up to 5 ounces. The fundamental observation you need to make for this problem is that each weight in the array can be either: 3. Put on the opposite side of the balance from the sample 4. Put on the same side of the balance as the sample 5. Left off the balance entirely If you consider one of the weights in the array and determine how choosing one of these three options affects the rest of the problem, you should be able to come up with the recursive insight you need to solve the problem. Problem 5: List Mnemonics On the standard Touch-Tone telephone dial, the digits are mapped onto the alphabet (minus the letters Q and Z) as shown in the diagram below: PICT-05-30
AC B DF E

1
GI H

2
JK L

3
MO N

4
PS R

5
TU V

6
WY X

8 0

In order to make their phone numbers more memorable, service providers like to find numbers that spell out some word (called a mnemonic) appropriate to their business that makes that phone number easier to remember. For example, the phone number for a recorded time-of-day message in some localities is 637-8687 (NERVOUS).

w w w
w

PD

PD

F-

XC

h a n g e Vi e

F-

XC

h a n g e Vi e

er

er

O W

bu

lic

lic

4
w
.c

to

to

bu

N
w

O W
.d o
c u -tr a c k

.d o

c u -tr a c k

.c

Imagine that you have just been hired by a local telephone company to write a function ListMnemonics that will generate all possible letter combinations that correspond to a given number, represented as a string of digits. For example, if you call
ListMnemonics("723")

your program should generate the following 27 possible letter combinations that correspond to that prefix: PAD PBD PCD RAD RBD RCD SAD SBD SCD PAE PBE PCE RAE RBE RCE SAE SBE SCE PAF PBF PCF RAF RBF RCF SAF SBF SCF

Problem 6: Spot the Bug While debugging some code, you narrow your search down to the following DoubleArrayLength function. Unfortunately, because pointers and arrays are rather difficult to display in a debugger, youre having some trouble finding the bug. You decide instead to trace the function by hand to see if you can spot the bug. Trace through a call to the following function to find the bug. Once you have found the bug, determine what should be done to fix it.
/* The static { int for { following function doubles the length of an array of integers */ void DoubleArrayLength(int *array, int length) *newArray = new int[length * 2]; (int i = 0; i < length; i++)

newArray[i] = array[i]; } for (int i = length; i < length * 2; i++) { newArray[i] = 0; } array = newArray; } int main() { int *myArray = new int[5]; for (int i = 0; i < 5; i++) { myArray[i] = i; } DoubleArrayLength(myArray, 5); cout << myArray[7]; return 0; }

w w w
w

PD

PD

F-

XC

h a n g e Vi e

F-

XC

h a n g e Vi e

er

er

O W

bu

to

lic

lic

to

bu

N
.c

O W
w
.d o
c u -tr a c k

.d o

c u -tr a c k

.c

CS106B Spring 03-04

Handout #11S April 12, 04

Section Solutions #2
Problem 1: Cannonballs
/* * Function: Cannonball * Usage: n = Cannonball(height); * -----------------------------* This function computes the number of cannonballs in a stack * that has been arranged to form a pyramid with one cannonball * at the top sitting on top of a square composed of four * cannonballs sitting on top of a square composed of nine * cannonballs, and so forth. The function Cannonball computes * the total number based on the height of the stack. */ static int Cannonball(int height) { if (height == 0) { return (0); } else { return (height * height + Cannonball(height - 1)); } }

Problem 2: ReverseString
static string ReverseStringRecursive (string str) { if (str.length() == 0) { return ""; } return ReverseStringRecursive(str.substr(1)) + str[0]; } static string ReverseStringIterative (string str) { string result = ""; for (int i = str.length() - 1; i >= 0; i--) { result += str[i]; } return result; }

Which one is easier to come up with? The recursive one, of course!

w w w
w

w
w

PD

PD

F-

XC

h a n g e Vi e

F-

XC

h a n g e Vi e

er

er

O W

bu

lic

lic

2
w
.c

to

to

bu

N
w

O W
.d o
c u -tr a c k

.d o

c u -tr a c k

.c

Problem 3: GCD
static int GCD(int x, int y) { if ((x % y) == 0) { return y; } else { return GCD (y, x % y); } }

Problem 4: Old-Fashioned Measuring (Chapter 5, Programming Exercise 10)


static bool IsMeasurable(int target, int weights[], int nWeights) { return RecIsMeasurable(target, weights, nWeights, 0); } static bool IsMeasurable(int target, int weights[], int nWeights, int currentWeight) { if (target == 0) return true;

if (currentWeight >= nWeights) return false;

return (RecIsMeasurable(target + weights[currentWeight], weights, nWeights, currentWeight + 1) || RecIsMeasurable(target, weights, nWeights, currentWeight + 1) || RecIsMeasurable(target - weights[currentWeight], weights, nWeights, currentWeight + 1)); }

w w w
w

PD

PD

F-

XC

h a n g e Vi e

F-

XC

h a n g e Vi e

er

er

O W

bu

lic

lic

3
w
.c

to

to

bu

N
w

O W
.d o
c u -tr a c k

.d o

c u -tr a c k

.c

Problem 5: List Mnemonics


/* * Function: ListMnemonics * Usage: ListMnemonics(str); * -------------------------* This function lists all of the mnemonics for the string of digits * stored in the string str. The correspondence between digits and * letters is the same as that on the standard telephone dial. The * implementation at this level is a simple wrapper function that * provides the arguments necessary for the recursive call. */ static void ListMnemonics(string str) { RecursiveMnemonics("", str); }

/* * Function: RecursiveMnemonics * Usage: RecursiveMnemonics(prefix, rest); * ---------------------------------------* This function does all of the real work for ListMnemonics and * implements a more general problem with a recursive solution * that is easier to see. The call to RecursiveMnemonics generates * all mnemonics for the digits in the string rest prefixed by the * mnemonic string in prefix. As the recursion proceeds, the rest * string gets shorter and the prefix string gets longer. */ static void RecursiveMnemonics(string prefix, string rest) { if (StringLength(rest) == 0) { cout << prefix << endl; } else { string options = DigitLetters(prefix[0]); for (i = 0; i < options.length(); i++) { RecursiveMnemonics(prefix + options[i], rest.substr(1)); } } }

w w w
w

PD

PD

F-

XC

h a n g e Vi e

F-

XC

h a n g e Vi e

er

er

O W

bu

lic

lic

4
w
.c

to

to

bu

N
w

O W
.d o
c u -tr a c k

.d o

c u -tr a c k

.c

/* * Function: DigitLetters * Usage: digits = DigitLetters(ch); * --------------------------------* This function returns a string consisting of the legal * substitutions for a given digit character. Note that 0 and * 1 are handled just by leaving that digit in its position. */

static string DigitLetters(char ch) { switch (ch) { case '0': return ("0"); case '1': return ("1"); case '2': return ("ABC"); case '3': return ("DEF"); case '4': return ("GHI"); case '5': return ("JKL"); case '6': return ("MNO"); case '7': return ("PRS"); case '8': return ("TUV"); case '9': return ("WXY"); default: Error("Illegal digit"); } }

w w w
w

PD

PD

F-

XC

h a n g e Vi e

F-

XC

h a n g e Vi e

er

er

O W

bu

lic

lic

5
w
.c

to

to

bu

N
w

O W
.d o
c u -tr a c k

.d o

c u -tr a c k

.c

Problem 6: Spot the Bug The bug occurs because myArray is passed into DoubleArrayLength by value rather than by reference. This means that changes to the variable array in DoubleArrayLength are not seen by myArray in main. This seems a little counter intuitive since array is a pointer, and we say that a pointer allows changes to be seen by the caller. What happens is that any changes to elements of the array will be seen, but DoubleArrayLength changes not the elements of the array but where the array is in the first place. Here is a picture, in case that helps:

Stack
DoubleArrayLength

Heap

0 array

length

5
main

myArray

Note that the value of myArray itself has not been changed by a call to DoubleArrayLength. To fix this, we do the same thing that we always do to allow a function to change the variables passed to it in a way that the caller sees the changeswe use a reference! All we have to do is change the prototype to DoubleArrayLength to the following and all of our troubles go away:
static void DoubleArrayLength(int *&array, int length)

To help you understand an integer.

int *&array,

read it from the right.

array

is a reference to a pointer to

w w w
w

PD

PD

F-

XC

h a n g e Vi e

F-

XC

h a n g e Vi e

er

er

O W

bu

to

lic

lic

to

bu

N
.c

O W
w
.d o
c u -tr a c k

.d o

c u -tr a c k

.c

CS106B Winter 05-06

Handout #13 January 23, 2006

Section Handout #2
Problem 1: Memory Diagram a) Trace through the following bit of code and draw a diagram showing the contents of memory at the indicated point. Be sure to differentiate between the stack and the heap.
struct nameT { string name; int level; }; struct groupT { nameT lidda[2]; nameT *jozan; nameT *devis[2]; }; void Mialee(groupT tordek, int &level) { tordek.lidda[1].name = Hennet; tordek.lidda[1].level = 10; tordek.devis[0] = &tordek.jozan[1]; level += 5; tordek.devis[0]->level = level + tordek.devis[1]->level; tordek.devis[0]->name = tordek.lidda[1].name.substr(3, 2) + bit; /* DRAW THE STATE OF MEMORY HERE */ } int main() { groupT tordek; tordek.lidda[0].name = Ember; tordek.lidda[0].level = 2; tordek.lidda[1].name = Krusk; tordek.lidda[1].level = 3; tordek.jozan = new nameT[2]; tordek.devis[0] = NULL; tordek.devis[1] = &tordek.jozan[0]; *tordek.devis[1] = tordek.lidda[1]; Mialee(tordek, tordek.lidda[1].level); return 0; }

b) What would happen instead if tordek were passed by reference instead of by value? This would make the prototype for Mialee:
void Mialee(groupT &tordek, int &level)

w w w
w

w
w

PD

PD

F-

XC

h a n g e Vi e

F-

XC

h a n g e Vi e

er

er

O W

bu

lic

lic

2
w
.c

to

to

bu

N
w

O W
.d o
c u -tr a c k

.d o

c u -tr a c k

.c

Problem 2: Arrays Write a function CountLetters that takes an array of strings and counts the number of times each letter of the alphabet appears. Because there are 26 numbers to be returned, CountLetters needs to return an array. For example, if we provide the following array: abcd ijk cabbage fad

CountLetters should return the following:

Note that this array should be 26 elements long, but that we couldn't easily display that on the page. Before writing the CountLetters function, you should decide if the array it returns needs to be dynamically allocated. You should use the following prototype, and you may assume that the array you are passed contains only lowercase letters and no other characters.
int *CountLetters(string words[], int numWords);

Problem 3: Spot the Bug While debugging some code, you narrow your search down to the following DoubleArrayLength function. Trace through a call to the following function to find the bug. Once you have found the bug, determine what should be done to fix it.
/* The following function doubles the length of an array of integers */ void DoubleArrayLength(int *array, int length) { int *newArray = new int[length * 2]; for (int i = 0; i < length; i++) { newArray[i] = array[i]; } for (int i = length; i < length * 2; i++) { newArray[i] = 0; } array = newArray; } int main() { int *myArray = new int[5]; for (int i = 0; i < 5; i++) { myArray[i] = i; } DoubleArrayLength(myArray, 5); cout << myArray[7];

w w w
w

PD

PD

F-

XC

h a n g e Vi e

F-

XC

h a n g e Vi e

er

er

O W

bu

lic

lic

3
w
.c

to

to

bu

N
w

O W
.d o
c u -tr a c k

.d o

c u -tr a c k

.c

return 0; }

Problem 4: What kind of structures do you like? You need to store a collection of a particular type of structure. In general, think about the differences between declaring a static array of the structures, a dynamic array of those structures, and a static array of pointers to those structures. In what cases would you want to use each of these approaches?

Problem 5: Data Structures You're going to read credit card billing information from a file that is organized like this:
Kermit the Frog name of credit card holder 12 number of charges this month Macy's proprietor for a charge 123.45 amount of charge Tower Records etc. 45.12 .... 10 more charges here ... Bill Gates next record follows immediately after the end 120 Fry's Electronics 12345.60 ... etc. ...

A blank line will follow the last client in the file to signify the end. There can be up to MAX_RECORDS records in the file, but is likely to be a lot less, so you don't really want to pre-allocate any records. You will have a complete array of struct pointers, and you should use new to create actual structures only when needed. There is no limit on the number of charges that may be billed for each person. Design the data structure for this database and write the functions needed to read this information from the file. How would you free the memory allocated for the data structure you designed?

w w w
w

PD

PD

F-

XC

h a n g e Vi e

F-

XC

h a n g e Vi e

er

er

O W

bu

lic

lic

4
w
.c

to

to

bu

N
w

O W
.d o
c u -tr a c k

.d o

c u -tr a c k

.c

Problem 6: Cannonballs Suppose that you have somehow been transported back to 1777 and the Revolutionary War. You have been assigned a dangerous reconnaissance mission: evaluate the amount of ammunition available to the British for use with their large cannon which has been shelling the Revolutionary forces. Fortunately for you, the Britishbeing neat and orderlyhave stacked the cannonballs into a single pyramid-shaped stack with one cannonball at the top, sitting on top of a square composed of four cannonballs, sitting on top of a square composed of nine cannonballs, and so forth. Unfortunately, however, the Redcoats are also vigilant, and you only have time to count the number of layers in the pyramid before you are able to escape back to your own troops. To make matters worse, computers will not be invented for at least 150 years, but you should not let that detail get in your way. Your mission is to write a recursive function Cannonball that takes as its argument the height of the pyramid and returns the number of cannonballs therein.
int Cannonball(int height);

Problem 7: GCD The greatest common divisor (g.c.d.) of two nonnegative integers is the largest integer that divides evenly into both. In the third century B.C., the Greek mathematician Euclid discovered that the greatest common divisor of x and y can always be computed as follows: If x is evenly divisible by y, then y is the greatest common divisor. Otherwise, the greatest common divisor of x and y is always equal to the greatest common divisor of y and the remainder of x divided by y. Use Euclid's insight to write a recursive function int GCD(int x, int y) that computes the greatest common divisor of x and y.

w w w
w

PD

PD

F-

XC

h a n g e Vi e

F-

XC

h a n g e Vi e

er

er

O W

bu

to

lic

lic

to

bu

N
.c

O W
w
.d o
c u -tr a c k

.d o

c u -tr a c k

.c

CS106B Winter 05-06

Handout #13S January 23, 2006

Section Solutions #2
Problem 1: Memory Diagram

STACK

HEAP

main tordek name name lidda


Ember Krusk

name
nebit

name
Krusk

level level
2

level
11

level
3 8

jozan

devis

Mialee tordek

name
Ember

name
Hennet

lidda level
2

level
10

jozan devis

level

w w w
w

w
w

PD

PD

F-

XC

h a n g e Vi e

F-

XC

h a n g e Vi e

er

er

O W

bu

to

lic

lic

to

bu

N
.c

O W
w
.d o
c u -tr a c k

.d o

c u -tr a c k

.c

b) If tordek were passed by reference, all of the changes made to tordek inside of Mialee would be seen by main. It is also important to note that this would have some interesting effects upon tordek.lidda[1].level. Once tordek is passed by reference, both tordek.lidda[1].level and level would refer to the same location in memory, so essentially, changing one would change the other.

STACK

HEAP

main tordek name name lidda


Ember

name
nebit

name
Hennet Krusk

level level
2

level
18

level
15 3

jozan

devis

Mialee

tordek

level

w w w
w

w
w

PD

PD

F-

XC

h a n g e Vi e

F-

XC

h a n g e Vi e

er

er

O W

bu

to

lic

lic

to

bu

N
.c

O W
w
.d o
c u -tr a c k

.d o

c u -tr a c k

.c

Problem 2: Arrays To be able to return an array, it needs to be created dynamically. Remember that an array is simply a pointer, so if we return a static array, we are really just returning a pointer to memory on the stack which is deallocated when the function ends.
#define ALPHABET_SIZE 26 int *CountLetters(string words[], int numWords) { int *result = new int[ALPHABET_SIZE]; for (int i = 0; i < ALPHABET_SIZE; i++) result[i] = 0; // must initialize contents! for (int i = 0; i < numWords; i++) { for (int j = 0; j < words[i].length(); j++) { int index = words[i][j] a; result[index]++; } } return result; }

Problem 3: Spot the Bug The bug occurs because myArray is passed into DoubleArrayLength by value rather than by reference. This means that changes to the variable array in DoubleArrayLength are not seen by myArray in main. This seems a little counter intuitive since array is a pointer, and we say that a pointer allows changes to be seen by the caller. What happens is that any changes to elements of the array will be seen, but DoubleArrayLength changes not the elements of the array but where the array is in the first place. Here is a picture, in case that helps:

STACK
DoubleArrayLength array 0

HEAP

length

5 0 1 2 3 4

main myArray

w w w
w

w
w

PD

PD

F-

XC

h a n g e Vi e

F-

XC

h a n g e Vi e

er

er

O W

bu

to

lic

lic

to

bu

N
.c

O W
w
.d o
c u -tr a c k

.d o

c u -tr a c k

.c

Note that the value of myArray itself has not been changed by a call to DoubleArrayLength. To fix this, we do the same thing that we always do to allow a function to change the variables passed to it in a way that the caller sees the changeswe use a reference! All we have to do is change the prototype to DoubleArrayLength to the following and all of our troubles go away:
static void DoubleArrayLength(int *&array, int length)

To help you understand int pointer to an integer.

*&array,

read it from the right. array is a reference to a

Problem 4: What kind of structures do you like? Static array: + No dynamic allocation, don't have to work with pointers, can grow/shrink number of elements used (within bounds of the array) - The fixed upper bound means potentially too small or too large, can waste a lot of space or limit utility value + Exactly the size you need, as little or as large - Have to remember to allocate & thus must know size in advance, can't grow/shrink easily once allocated + Moderately conservative in use of memory, can grow/shrink within bounds of array, pointers are easier to swap and move around inside array - Lots of allocations means lots of opportunities to forget, still have fixed upper limit problems, forces you to deal with pointers

Dynamic array:

Static array of ptrs:

Any decision among these three choices should consider such factors as the size of the structure itself, how large the variance is in the number of elements needed, how comfortable you feel about working with pointers, whether this is a known upper bound that is never exceeded, whether you need to grow and shrink the array, and so on.

w w w
w

w
w

PD

PD

F-

XC

h a n g e Vi e

F-

XC

h a n g e Vi e

er

er

O W

bu

to

lic

lic

to

bu

N
.c

O W
w
.d o
c u -tr a c k

.d o

c u -tr a c k

.c

Problem 5: Data Structures


struct chargeRec { string proprietor; double amount; }; struct cardHolder { string name; chargeRec *chargeArray; // dynamically-allocated array of charges int numCharges; // num elements in above array }; struct billingDB { cardHolder *cardHolderArray[MAX_RECORDS]; // array of ptr to // cardHolders int numCardHolders; // effective size of above array }; chargeRec ReadCharge(ifstream& in) { chargeRec charge; string line; getline(in, charge.proprietor); getline(in, line); charge.amount = StringToReal(line); return charge; } // The reading and freeing functions are on the next page cardHolder *ReadCardHolder(ifstream& in) { string line; cardHolder *client; getline(in, line); if (line == "") return NULL; client = new cardHolder; client->name = line; // makes a copy of the line getline(in, line); client->numCharges = StringToInteger(line); /* dynamically allocate the charge array now that we know the size */ client->chargeArray = new chargeRec[client->numCharges]; for (int i = 0; i < client->numCharges; i++) { client->chargeArray[i] = ReadCharge(in); } return client;

w w w
w

w
w

PD

PD

F-

XC

h a n g e Vi e

F-

XC

h a n g e Vi e

er

er

O W

bu

to

lic

lic

to

bu

N
.c

O W
w
.d o
c u -tr a c k

.d o

c u -tr a c k

.c

} billingDB *ReadBillingDB(ifstream& in) { cardHolder *client; billingDB *db; db = new billingDB; db->numCardHolders = 0; for (int i = 0; i < MAX_RECORDS; i++) { client = ReadCardHolder(in); if (client == NULL) break; // no more clients db->cardHolderArray[i] = client; db->numCardHolders++; } return db; }

This data structure has lots of storage to free the charge arrays themselves, the card holder records, etc. Be sure to free things in the correct order (free things pointed to out of structures before freeing those structures themselves).
void FreeCardHolder(cardHolder *client) { delete[] client->chargeArray; // free dynamic array itself delete client; // free cardHolder structure } void FreeBillingDB(billingDB *db) { for (int i = 0; i < db->numCardHolders; i++) { FreeCardHolder(db->cardHolderArray[i]); } delete db; }

w w w
w

w
w

PD

PD

F-

XC

h a n g e Vi e

F-

XC

h a n g e Vi e

er

er

O W

bu

to

lic

lic

to

bu

N
.c

O W
w
.d o
c u -tr a c k

.d o

c u -tr a c k

.c

Problem 6: Cannonballs
/* * Function: Cannonball * Usage: n = Cannonball(height); * -----------------------------* This function computes the number of cannonballs in a stack * that has been arranged to form a pyramid with one cannonball * at the top sitting on top of a square composed of four * cannonballs sitting on top of a square composed of nine * cannonballs, and so forth. The function Cannonball computes * the total number based on the height of the stack. */ int Cannonball(int height) { if (height == 0) { return (0); } else { return (height * height + Cannonball(height - 1)); } }

Problem 7: GCD
int GCD(int x, int y) { if ((x % y) == 0) { return y; } else { return GCD (y, x % y); } }

w w w
w

w
w

PD

PD

F-

XC

h a n g e Vi e

F-

XC

h a n g e Vi e

er

er

O W

bu

to

lic

lic

to

bu

N
.c

O W
w
.d o
c u -tr a c k

.d o

c u -tr a c k

.c

CS106B Spring 05-06

Handout #14 April 19, 2006

Section Handout #2
Problem 1: Using the Scanner and Stack classes
<html><b><i>CS106 rules!</i></b></html>

Web browsers use stacks to track html tags such as <b>, <i> or <html>. Every html tag must be matched by an equivalent closing tag -- <b>, <i> or <html>. Microsoft is looking for programmers to help implement this feature in the next version of Internet Explorer and you, armed with your newly acquired knowledge of classes, decide to volunteer for the job. Using the Scanner class (from A-21 in the reader) and the Stack class (A-27), write the following function:
bool IsCorrectlyNested(string htmlStr);

You can assume that all the tags in the html string will be correctly formed. That is, once you see an angle bracket, it will be followed by the remainder of a complete and wellformed tag (So, nothing like <<html>). Problem 2: Queues Write a function
void ReverseQueue(Queue<int> *q);

that reverses the elements in the passed in queue. Moreover, because ElementAt makes this part of the exercise too easy, write your implementation of ReverseQueue without using ElementAt. (Hint: Is there another class that could make doing this a lot easier?) Problem 3: Vectors (AKA C++ ArrayLists) Say we are writing the next version of Eudora and want to use a Vector (A-31) to store all the data. The following structure is used to hold the data of an email message:
struct eMailMsg { string to; // i.e. "professor@stanford.edu" string from; // i.e. "student@stanford.edu" string message; // body of message string subject; // i.e. "I really like your class, but not as much // as CS106" int date; // date email was sent int time; // time email was sent };

a) How would you declare and allocate memory for a Vector that stores eMailMsgs? b) Write a function RemoveSpam that takes a vector containing elements of type eMailMsg and removes all elements whose subject begins with the string "SPAM".

w w w
w

w
w

PD

PD

F-

XC

h a n g e Vi e

F-

XC

h a n g e Vi e

er

er

O W

bu

lic

lic

2
w
.c

to

to

bu

N
w

O W
.d o
c u -tr a c k

.d o

c u -tr a c k

.c

c) How could you modify the to field of the eMailMsg structure so that it can hold the email addresses of an arbitrary number of recipients of an email? With the modification in place, given an eMailMsg email, how would you access the last address listed in the to field? Problem 4: Data Structure Design Apple has decided to release new software for the iPod, and since everyone in 106B has just learned about data structures and pointers, decides to ask for your help with the design decisions. For simplicity, each song only needs to store the album, artist, and genre associated with it. a.) How would you design the data structure to represent a song and the list of songs stored on an iPod? b.) Just viewing songs is fun, but Apple also wants the user to be able to have multiple views on the same data. For instance, the user should be able to view by album, by artist or by genre. How would you create the data structures to do this? c.) Now suppose the user connects their iPod to their computer and a song needs to be added to the iPod. What needs to be updated in the data structures to allow this to happen? Also, what happens in the case of deleting? Does your design from parts a and b easily allow you to add and delete? If not, what changes would you make so that these operations would be easier/more efficient? Problem 5: Symbol Table Warm-up Write a function:
char MostFrequentCharacter(ifstream &if, int &numOccurrences);

that given an input file stream, returns the character that occurs the most frequently and stores the number of times it occurs in the reference parameter numOccurrences. To write this function, first start by scanning through the file stream, analyzing each character and storing an updated count in a symbol table. Then, after youve built this table, iterate over it to find the character that occurred the most often. (Note that unlike last weeks problem of counting the letters in an array of strings, by using the symbol table we dont need to restrict ourselves to just lowercase letters.)

w w w
w

PD

PD

F-

XC

h a n g e Vi e

F-

XC

h a n g e Vi e

er

er

O W

bu

to

lic

lic

to

bu

N
.c

O W
w
.d o
c u -tr a c k

.d o

c u -tr a c k

.c

CS106B Spring 05-06

Handout #14S April 19, 2006

Section Solutions #2
Problem 1: Using the Scanner and Stack classes
#include "stack.h" #include "scanner.h" bool ProcessOpenTag(Scanner *scanner, Stack<string> *tagStack) { string tag = scanner->ReadToken(); tagStack->Push(tag); } return true;

bool ProcessCloseTag(Scanner *scanner, Stack<string> *tagStack) { string tag = scanner->ReadToken(); if (!tagStack->IsEmpty() && tag == tagStack->Pop()) { return true; }else { return false; }

bool ProcessTag(Scanner *scanner, Stack<string> *tagStack) { // read the next token to see if we found an // opening or closing tag string token = scanner->ReadToken(); if (token == "/") { return ProcessCloseTag(scanner, tagStack); } else { scanner->SaveToken(token); return ProcessOpenTag(scanner, tagStack); }

bool IsCorrectlyNested(string htmlStr) { Scanner *scanner = new Scanner(); scanner->SetSpaceOption(Scanner::IgnoreSpaces); Stack<string> *tagStack = new Stack<string>(); scanner->SetScannerString(htmlStr); // start by assuming it is balanced

w w w
w

w
w

PD

PD

F-

XC

h a n g e Vi e

F-

XC

h a n g e Vi e

er

er

O W

bu

to

lic

lic

to

bu

N
.c

O W
w
.d o
c u -tr a c k

.d o

c u -tr a c k

.c

bool isBalanced = true; while (scanner->MoreTokensExist()) { string token = scanner->ReadToken(); if (token == "<") { if (!ProcessTag(scanner, tagStack)) { isBalanced = false; break; } // get rid of ">" part of tag scanner->ReadToken();

if (!tagStack->IsEmpty()) isBalanced = false; delete scanner; delete tagStack; return isBalanced;

Problem 2: Queues
/** * The client version of reverse queue. In order * to change the order of elements in the queue, * we use an external stack */ void ReverseQueue(Queue<int>* queue) { Stack<int>* stack = new Stack<int>(); while (!queue->IsEmpty()) stack->Push(queue->Dequeue()); while (!stack->IsEmpty()) queue->Enqueue(stack->Pop()); delete stack; }

Problem 3: Vectors a) Vector<eMailMsg> *mailVector = new Vector<eMailMsg>(); b)


void RemoveSpam(Vector<eMailMsg> *v) { for (int i = 0; i < v->Length(); i++) { eMailMsg mail = v->ElementAt(i); if (mail.subject.find("SPAM") == 0) { v->RemoveAt(i); i--; // look at this index again, // since elements have been shifted down } }

w w w
w

w
w

PD

PD

F-

XC

h a n g e Vi e

F-

XC

h a n g e Vi e

er

er

O W

bu

to

lic

lic

to

bu

N
.c

O W
w
.d o
c u -tr a c k

.d o

c u -tr a c k

.c

Note that you also could move backwards down the vector and not have to worry about decrementing i. c) We use another Vector, of course!
struct eMailMsg { Vector<string> *to; string from; string message; string subject; int date; int time; };

Access to the last element of eMailMsg email would be done by:


string lastAddress = email.to->ElementAt(email.to->Length() 1);

Problem 4: Data Structure Design a.) To store song information we could create something as follows:
struct songT { string string string string };

name; album; artist; genre;

char *data;

and then to store the collection of songs, we could simply turn to the Vector class:
Vector<songT *> *songs;

b.) To create multiple views, we could define our data structures as below:
struct albumT { string albumName; }; Vector<songT *> *songs;

struct artistT { string artistName; }; Vector<songT *> *songs;

w w w
w

w
w

PD

PD

F-

XC

h a n g e Vi e

F-

XC

h a n g e Vi e

er

er

O W

bu

to

lic

lic

to

bu

N
.c

O W
w
.d o
c u -tr a c k

.d o

c u -tr a c k

.c

typedef enum genreTypeT { Rap, Rock, Jazz, Classical }; struct genreT { genreTypeT genre; }; Vector<songT *> *songs;

And then to have multiple views on the data, we simply need to store Vectors of the above information:
Vector<artistT *> *artists; Vector<albumT *> *albums; Vector<genreT *> *genres;

In each of the above, we make heavy use of the Vector to help store all of our data. Note that in all of the structures, we store a pointer to the songT when storing a list of songs. This lets us just have one copy of each song, and then reuse the data associated with that song. If each of the above structures just stored a songT in their vectors rather than a pointer to one, then they each would have a copy of the contents of the songT. This would cause us to store a lot of redundant data Also, if the song name was accidentally mistyped, we only need to change the data in that song for everyone else to notice the change, rather than having to search through everything to find matches to that song. It also might be helpful to store all of the vectors in sorted order. This would allow for convenient browsing, but also would mean wed need to do a little extra work to make sure the array was sorted (for instance, by inserting new songs in to the array in their correct alphabetic position). Also, with these new data structures, we can change up our songT slightly. Rather than storing the name of the album, artist and genre, we can leverage the fact that we now have data structures to store this information, and can point to the associated data structure for each field. Now, if we have a song and want to find the other songs in the same genre, we simply need to follow a pointer to find out all this information.
struct songT { string name; albumT *album; artistT *artist; genreT *genre; }; char *data;

c.) To add a song, we need to create a new songT. To do so well need to find the album, artist and genre the song is associated with (or create them if they dont exist). After which well want to add the songT to the list of songs, as well as to the album, artist and genre it belongs to. To delete a song we simply need to look it up, and them remove it

w w w
w

w
w

PD

PD

F-

XC

h a n g e Vi e

F-

XC

h a n g e Vi e

er

er

O W

bu

to

lic

lic

to

bu

N
.c

O W
w
.d o
c u -tr a c k

.d o

c u -tr a c k

.c

from all the views it is associated with. Our use of the Vector means all of these operations are fairly easy. If we chose to store raw arrays, wed have a lot more work to do in terms of organizing our data: wed have to make sure to have enough memory allocated, shuffle array elements depending on where we inserted or deleted, make sure to stay in bounds, etc Problem 5: Symbol Table Warm-up
char MostFrequentCharacter(ifstream &in, int &numOccurrences) { Symtab<int> *charFrequencies = new Symtab<int>; while (true) { // get the next character from the stream int nextChar = in.get(); if (nextChar == EOF) { break; } // convert it to a string for lookup in the symbol table string foundChar = ""; foundChar += char(nextChar); // if we find it, incremement the stored value, otherwise // enter in a new one int frequency = 1; if (charFrequencies->Lookup(foundChar, frequency)) { charFrequencies->Enter(foundChar, frequency + 1); } else { charFrequencies->Enter(foundChar, frequency); }

// now use an iterator to find the most occurring character Iterator<string> *it = charFrequencies->CreateIterator(); string maxCharacter = ""; while (it->HasNext()) { string character = it->Next(); int frequency; charFrequencies->Lookup(character, frequency); if (frequency > numOccurrences) { maxCharacter = character; numOccurrences = frequency; }

w w w
w

w
w

PD

PD

F-

XC

h a n g e Vi e

F-

XC

h a n g e Vi e

er

er

O W

bu

to

lic

lic

to

bu

N
.c

O W
w
.d o
c u -tr a c k

.d o

c u -tr a c k

.c

delete it; delete charFrequencies; } return maxCharacter[0];

w w w
w

w
w

PD

PD

F-

XC

h a n g e Vi e

F-

XC

h a n g e Vi e

er

er

O W

bu

to

lic

lic

to

bu

N
.c

O W
w
.d o
c u -tr a c k

.d o

c u -tr a c k

.c

CS106B Spring 06-07

Handout #16 April 16, 2007

Section Handout #2
Problem 1: Vectors (AKA C++ ArrayLists) Say we are writing the next version of Eudora and want to use a Vector (interface in reader appendix) to store all the data. The following structure is used to hold the data of an email message:
struct eMailMsg { string to; string from; string message; string subject; int date; int time; }; // // // // // // i.e. i.e. body i.e. date time "professor@stanford.edu" "student@stanford.edu" of message "CS106 Rocks!" email was sent email was sent

a) How would you declare a Vector that stores eMailMsgs? b) Write a function RemoveSpam that takes a vector containing elements of type eMailMsg and removes all elements whose subject begins with the string "SPAM". c) How could you modify the to field of the eMailMsg structure so that it can hold the email addresses of an arbitrary number of recipients of an email? With the modification in place, given an eMailMsg email, how would you access the last address listed in the to field? Problem 2: Queues Write a function
void ReverseQueue(Queue<int> & q);

that reverses the elements in the passed in queue. (Hint: Is there another class that could make doing this a lot easier?) Problem 3: Map Warm-up Write a function:
char MostFrequentCharacter(ifstream & if, int & numOccurrences);

that given an input file stream, returns the character that occurs the most frequently and stores the number of times it occurs in the reference parameter numOccurrences. To write this function, first start by scanning through the file stream, analyzing each character and storing an updated count in a map. Then, after youve built this table, iterate over it to find the character that occurred the most often.

w w w
w

w
w

PD

PD

F-

XC

h a n g e Vi e

F-

XC

h a n g e Vi e

er

er

O W

bu

lic

lic

2
w
.c

to

to

bu

N
w

O W
.d o
c u -tr a c k

.d o

c u -tr a c k

.c

Problem 4: Crossword Puzzle A crossword puzzle solution is represented as a grid of characters, using an asterisk to indicate blackout squares, as shown in the example below:
(0,0) * * * * * * * M E * N * A N E C R U * N O S H O T * T O S O O T H E S * * M Y * * E * L * * * E * R O A D * A * O C D * * T * T I V O L I * * * A L A N * S Y N E R G Y H O T * C * A A D A G E * C V E G A N * H E L E G Y * T * * * * * * *

You are to write the function AcrossWords that builds a vector containing the words that read across in the puzzle solution. Each sequence of two or more characters bounded by asterisks forms a word. For example, in the puzzle above, the words "MESS", "ROT", and "SHAVE" read across the top row. There will always be an asterisk in the first and last columns of every row of the puzzle. The one parameter to AcrossWords is the grid with the puzzle solution (passed by reference.) The function returns a vector of strings where the vector elements are the words that read across in the order found when processing the puzzle from top-left to bottom-right.
Vector<string> AcrossWords(Grid<char> & puzzle)

The Grid interface is found in the reader's appendix.

w w w
w

PD

PD

F-

XC

h a n g e Vi e

F-

XC

h a n g e Vi e

er

er

O W

bu

to

lic

lic

to

bu

N
.c

O W
w
.d o
c u -tr a c k

.d o

c u -tr a c k

.c

CS106B Spring 06-07

Handout #16S April 18, 2007

Section Solutions #2
Problem 1: Vectors a) Vector<eMailMsg> mailVector; b)
void RemoveSpam(Vector<eMailMsg> & v) { for (int i = 0; i < v.size(); i++) { eMailMsg mail = v[i]; if (mail.subject.find("SPAM") == 0) { v.removeAt(i); i--; // look at this index again, // since elements have been shifted down } } }

Note that you also could move backwards down the vector and not have to worry about decrementing i. c) We use another Vector, of course!
struct eMailMsg { Vector<string> to; string from; string message; string subject; int date; int time; };

Access to the last element of eMailMsg email would be done by:


string lastAddress = email.to[email.to.size() 1];

Problem 2: Queues
/** * The client version of reverse queue. In order * to change the order of elements in the queue, * we use an external stack */ void ReverseQueue(Queue<int> & queue) { Stack<int> stack; while (!queue.isEmpty()) { stack.push(queue.dequeue()); } while (!stack.isEmpty())

w w w
w

w
w

PD

PD

F-

XC

h a n g e Vi e

F-

XC

h a n g e Vi e

er

er

O W

bu

to

lic

lic

to

bu

N
.c

O W
w
.d o
c u -tr a c k

.d o

c u -tr a c k

.c

{ queue.enqueue(stack.pop()); } }

Problem 3: Symbol Table Warm-up


void CountOccurrences(ifstream & in, Map<int> & charFrequencies) { while (true) { // Note: we need to store the result in an int, not a char int nextChar = in.get(); if (nextChar == EOF) { break; } // convert it to a string for lookup in the symbol table string foundChar = ""; foundChar += char(nextChar); int frequency = 1; if (charFrequencies.containsKey(foundChar)) { frequency = charFrequencies[foundChar] + 1; } charFrequencies[foundChar] = frequency; } } char MostFrequentCharacter(ifstream & in, int & numOccurrences) { Map<int> charFrequencies; numOccurrences = 0; CountOccurrences(in, charFrequencies); Map<int>::Iterator it = charFrequencies.iterator(); string maxCharacter = ""; while (it.hasNext()) { string character = it.next(); int frequency = charFrequencies[character]; if (frequency > numOccurrences) { maxCharacter = character; numOccurrences = frequency; } } return maxCharacter[0]; }

w w w
w

w
w

PD

PD

F-

XC

h a n g e Vi e

F-

XC

h a n g e Vi e

er

er

O W

bu

to

lic

lic

to

bu

N
.c

O W
w
.d o
c u -tr a c k

.d o

c u -tr a c k

.c

Problem 4: Crossword Puzzle


Vector<string> AcrossWords(Grid<char> & puzzle) { Vector<string> words; for(int row = 0; row < puzzle.numRows(); row++) { bool buildingWord = false; string curWord = ""; for(int col = 0; col < puzzle.numCols(); col++) { char ch = puzzle(row, col); if(ch == '*') { //Are we building a word? If so, store and reset if(buildingWord) { if(curWord.length() >= 2) { words.add(curWord); } curWord = ""; buildingWord = false; } } else { buildingWord = true; curWord += ch; } } } return words; }

w w w
w

w
w

PD

PD

F-

XC

h a n g e Vi e

F-

XC

h a n g e Vi e

er

er

O W

bu

to

lic

lic

to

bu

N
.c

O W
w
.d o
c u -tr a c k

.d o

c u -tr a c k

.c

Mehran Sahami CS106B

Handout #19 April 14, 2008

Section Handout #2
Problem 1: Quick Pointer Questions. Take this crash course test on pointers and memory: which of the following would cause compile-time or run-time problems? Also, what types are the left and right hand sides of each expression?
double x, *px, a[5]; x = *px; *px = x; px = &x; &x = px; &(x+1) = x; &x + 1 = x; *(&(x+1)) = x; *(&(x)+1) = x; x = a; x = a[0]; x = *(a[1]); x = (*a)[2]; x = a[3+1]; x = a[3] +1; x = &((a[3])+1); x = &(a[3]) +1; x = *(&(a[3])+1); px = a; px = a[0]; px = &(a[4]); /* /* /* /* /* /* /* /* /* /* /* /* /* /* /* /* /* /* /* /* 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 */ */ */ */ */ */ */ */ */ */ */ */ */ */ */ */ */ */ */ */

w w w
w

w
w

PD

PD

F-

XC

h a n g e Vi e

F-

XC

h a n g e Vi e

er

er

O W

bu

lic

lic

2
w
.c

to

to

bu

N
w

O W
.d o
c u -tr a c k

.d o

c u -tr a c k

.c

Problem 2: Memory Diagram Draw a diagram showing the contents of memory at the indicated point. Be sure to differentiate between the stack and the heap.
void HousingDraw(double **flomo, int lag, double wilbur[], int* & roble) { int* stern; int** govCo; stern = new int[2]; govCo = &stern; stern[0] = sizeof(stern) / 3; stern[1] = stern[0] * 3; *flomo = new double[lag]; roble = new int[2]; wilbur++; *(wilbur + 1) = 4; for(lag = 0; lag < *wilbur; lag++) { wilbur[lag] = wilbur[lag] / 2; } roble = *govCo; } int main() { int* toyon; double *kimball, branner[3]; for(int i = 0; i < 3; i++) { branner[i] = 6 / (i + 1); } HousingDraw(&kimball, branner[2], branner, toyon);

Draw a diagram indicating the contents of memory at this point


return 0; }

w w w
w

PD

PD

F-

XC

h a n g e Vi e

F-

XC

h a n g e Vi e

er

er

O W

bu

lic

lic

3
w
.c

to

to

bu

N
w

O W
.d o
c u -tr a c k

.d o

c u -tr a c k

.c

Problem 3: 2-D Arrays Write a function Make2D that takes a one-dimensional array, a row size, and a column size, and returns a 2-D array from the numbers in the first array. Make sure that your function can deal with arrays of different dimensions. You can also assume that there will be enough data in the array to fill the 2-D array of the specified size (i.e. there will be at least row * col elements in the one-dimensional array) For example, if we provide the following array:
3.7 8.2 4.0 9.5 2.7 6.4 9.8 5.4 9.1 3.5 5.6 7.8 8.7 2.0 9.4 7.1

And the row and column dimensions 4 and 4, we should get a pointer to a dynamically allocated array that looks like this:

Heap
2.7 6.4 9.8 5.4

3.7 8.2 4.0 9.5

9.1 3.5

8.7 2.0 9.4 7.1

5.6 7.8

The prototype is as follows:


double** Make2D(double array[], int rows, int cols);

w w w
w

PD

PD

F-

XC

h a n g e Vi e

F-

XC

h a n g e Vi e

er

er

O W

bu

lic

lic

4
w
.c

to

to

bu

N
w

O W
.d o
c u -tr a c k

.d o

c u -tr a c k

.c

Problem 4: CD Troubles Consider the following declaration, which declares a type for a compact disc that is the title of the disc, as well as a dynamic array of the track titles:
struct CDtype { string title; string *trackTitles; int numTracks; };

Assume that we have a function void ReadCD(CDtype& cd) that will read in one CD worth of info from a file: read the CD title, dynamically allocate the array of track titles and read them in. Now, assume that we have two CDs that are identical, except for the title of the first song. We try creating the two CD structs using the following code, but something is fishy herewhat is it?
int main() { CDtype cd1, cd2; ReadCD(cd1); cd2 = cd1; cd2.trackTitles[0] = "Layla"; return 0; } // read all info about cd1 // make cd2 a copy of the cd1 struct // change 1st song in cd2

Problem 5: What kind of structures do you like? You need to store a collection of a particular type of structure. In general, think about the differences between declaring a static array of the structures, a dynamic array of those structures, and a static array of pointers to those structures. In what cases would you want to use each of these approaches?

w w w
w

PD

PD

F-

XC

h a n g e Vi e

F-

XC

h a n g e Vi e

er

er

O W

bu

lic

lic

5
w
.c

to

to

bu

N
w

O W
.d o
c u -tr a c k

.d o

c u -tr a c k

.c

Problem 6: Data Structures You're going to read credit card billing information from a file that is organized like this:
Kermit the Frog name of credit card holder 12 number of charges this month Macy's proprietor for a charge 123.45 amount of charge Tower Records etc. 45.12 .... 10 more charges here ... Bill Gates next record follows immediately after the end 120 Fry's Electronics 12345.60 ... etc. ...

A blank line will follow the last client in the file to signify the end. There can be up to MAX_RECORDS records in the file, but is likely to be a lot less, so you don't really want to pre-allocate any records. You will have a complete array of struct pointers, and you should use new to create actual structures only when needed. There is no limit on the number of charges that may be billed for each person. Design the data structure for this database and write the functions needed to read this information from the file. How would you free the memory allocated for the data structure you designed?

w w w
w

PD

PD

F-

XC

h a n g e Vi e

F-

XC

h a n g e Vi e

er

er

O W

bu

to

lic

lic

to

bu

N
.c

O W
w
.d o
c u -tr a c k

.d o

c u -tr a c k

.c

Mehran Sahami CS106B

Handout #19S April 14, 2008

Section Solutions #2
Problem 1: Quick Pointer Questions. Here are the answers for these questions legal means it will compile, and illegal means that the compiler will generate an error:
Number 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 Expression x = *px; *px = x; px = &x; &x = px; &(x+1) = x; &x + 1 = x; *(&(x+1)) = x; *(&(x)+1) = x; x x x x = = = = a; a[0]; *(a[1]); (*a)[2]; Compiles? Legal Legal Legal Illegal- &x cant be assigned Illegal same reason as #4 Illegal same reason as #4 Illegal cant take &(x + 1) Legal stores into the variable after x on the stack. This will cause run-time problems! Illegal types dont match Legal Illegal a[1] is not a pointer Illegal (*a) is a double, which means we cant use array references on it. Legal Legal Illegal cant take the address of a number or expression Illegal types dont match Legal means the same thing as x = a[4] Legal Illegal types dont match Legal Left side type double double double* N/A N/A N/A N/A double double double double double double double double double double double* double* double* Right side type double double double* double* double double double double double* double N/A N/A double double N/A double* double double* double double*

x = a[3+1]; x = a[3] +1; x = &((a[3])+1); x = &(a[3]) +1; x = *(&(a[3])+1); px = a; px = a[0]; px = &(a[4]);

w w w
w

w
w

PD

PD

F-

XC

h a n g e Vi e

F-

XC

h a n g e Vi e

er

er

O W

bu

lic

lic

2
w
.c

to

to

bu

N
w

O W
.d o
c u -tr a c k

.d o

c u -tr a c k

.c

Problem 2: Memory Diagram

Stack
main 1 toyon 3 kimball branner 6.0 1.5 2.0

Heap

??? ???

??? ???

Orphaned! Problem 3: 2-D Arrays Note that since C++ uses memory in a row-major format, we start by creating an array of double*'s to hold all the rows, and then fill in the numbers by column.
double** Make2D(double array[], int nRows, int nCols) { double** result; result = new double*[nRows]; for (int i = 0; i < nRows; i++) { result[i] = new double[nCols]; for (int j = 0; j < nCols; j++) { result[i][j] = array[(i * nCols) + j]; } } return result; }

Problem 4: CD Troubles The problem is that each CDtype variable contains a dynamic array of strings, which is actually a pointer. So when we copy CD1 into CD2, everything gets copied exactly, including the base address of the array, which means that both CD1 and CD2 are pointing to the same array of trackTitles. In changing the title of the first song on CD2, we will inadvertently change the title for CD1 also. In order to create a copy of a CDtype that is entirely distinct, we need to allocate memory for the new trackTitles array, and then copy each title individually. (This structure copy operation would make a really good function!)

w w w
w

PD

PD

F-

XC

h a n g e Vi e

F-

XC

h a n g e Vi e

er

er

O W

bu

lic

lic

3
w
.c

to

to

bu

N
w

O W
.d o
c u -tr a c k

.d o

c u -tr a c k

.c

Problem 5: What kind of structures do you like? Static array: Pros: No dynamic allocation, don't have to work with pointers, can use different number of elements in the array (within bounds of the array). Cons: The fixed upper bound means potentially too small or too large, can waste a lot of space or limit utility value. Dynamic array: Pros: Exactly the size you need, as little or as large. Cons: Have to remember to allocate memory for array before referring to the elements of the array, and the deallocate memory when you done with it. Static array of ptrs: Pros: Moderately conservative in use of memory, can use different number of elements in the array within bounds of array, pointers are easier to swap and move around inside array. Cons: Lots of allocations means lots of opportunities to forget, still have fixed upper limit problems, forces you to deal with pointers. Any decision among these three choices should consider such factors as the size of the structure itself, how large the variance is in the number of elements needed, how comfortable you feel about working with pointers, whether this is a known upper bound that is never exceeded, whether you need to grow and shrink the array, and so on.

w w w
w

PD

PD

F-

XC

h a n g e Vi e

F-

XC

h a n g e Vi e

er

er

O W

bu

lic

lic

4
w
.c

to

to

bu

N
w

O W
.d o
c u -tr a c k

.d o

c u -tr a c k

.c

Problem 6: Data Structures


struct chargeRec { string proprietor; double amount; }; struct cardholder { string name; int numCharges; chargeRec *chargeArray; // dynamically-allocated array of charges }; struct billingDB { int numCardHolders; cardHolder *cardHolderArray[MAX_RECORDS]; // array of ptr to cardHolders }; chargeRec ReadCharge(ifstream& in) { chargeRec charge; string line; getline(in, charge.proprietor); getline(in, line); charge.amount = StringToReal(line); return charge; } cardHolder *ReadCardHolder(ifstream& in) { string line; cardHolder *client; getline(in, line); if (line == "") return NULL; client = new cardHolder; client->name = line; // makes a copy of the line getline(in, line); client->numCharges = StringToInteger(line); /* dynamically allocate the charge array now that we know the size */ client->chargeArray = new chargeRec[client->numCharges]; for (int i = 0; i < client->numCharges; i++) { client->chargeArray[i] = ReadCharge(in); } return client; }

w w w
w

PD

PD

F-

XC

h a n g e Vi e

F-

XC

h a n g e Vi e

er

er

O W

bu

lic

lic

5
w
.c

to

to

bu

N
w

O W
.d o
c u -tr a c k

.d o

c u -tr a c k

.c

billingDB *ReadBillingDB(ifstream& in) { cardHolder *client; billingDB *db; db = new billingDB; db->numCardHolders = 0; for (int i = 0; i < MAX_RECORDS; i++) { client = ReadCardHolder(in); if (client == NULL) break; db->cardHolderArray[i] = client; db->numCardHolders++; } return db; }

// no more clients

This data structure has lots of storage to free the charge arrays themselves, the card holder records, etc. Be sure to free things in the correct order (free things pointed to out of structures before freeing those structures themselves).
void FreeCardHolder(cardHolder *client) { delete[] client->chargeArray; // free dynamic array itself delete client; // free cardHolder structure } void FreeBillingDB(billingDB *db) { for (int i = 0; i < db->numCardHolders; i++) { FreeCardHolder(db->cardHolderArray[i]); } delete db; }

w w w
w

PD

PD

F-

XC

h a n g e Vi e

F-

XC

h a n g e Vi e

er

er

O W

bu

to

lic

lic

to

bu

N
.c

O W
w
.d o
c u -tr a c k

.d o

c u -tr a c k

.c

Eric Roberts CS106B

Handout #17 April 13, 2009

Section Handout #2ADTs


Problem 1. Using grids In the game of Minesweeper, a player searches for hidden bombs on a rectangular grid. The game board is represented by a grid of booleans marking bomb locations. A grid value is true if there is bomb at that location, false otherwise. Here is an example grid:
(0,0) T F T T F F F F T F F F F F F F T F F F T F F F F F F F F F T T T F F F

Given such a grid of bomb locations, the function


void MakeGridOfCounts(Grid<bool> & locations, Grid<int> & counts);

should construct a grid of integers storing the count of bombs in each neighborhood. The neighborhood for a location includes the location itself and its eight adjacent locations, but only if they are inside the dimensions of the grid. The reference parameter counts is used to store the result; your job is to make sure that it has the same size as the locations grid and then to assign to each element an integer between 0 and 9. For example, if sampleBombLocations contains the boolean grid shown earlier, the code
Grid<int> sampleBombCounts; MakeGridOfCounts(sampleBombLocations, sampleBombCounts);

should initialize sampleBombCounts as follows:


(0,0)

1 3 3 3 1 0

1 3 3 4 2 1

0 2 2 3 1 1

0 1 1 2 1 1

2 4 3 2 0 0

2 3 2 1 0 0

Problem 2. Using queues (Chapter 4, exercise 9, page 170) Bob Dylans 1963 song The Times They Are A-Changin contains the following lines, which are themselves paraphrased from Matthew 19:30: And the first one now Will later be last For the times they are a-changin In keeping with this revolutionary sentiment, write a function
void ReverseQueue(Queue<string> & queue);

w w
w

w
w

PD

PD

F-

XC

h a n g e Vi e

F-

XC

h a n g e Vi e

er

er

O W

bu

lic

lic

2
w
.c

to

to

bu

N
w

O W
.d o
c u -tr a c k

.d o

c u -tr a c k

.c

that reverses the elements in the queue. Remember that you have no access to the internal representation of the queue and will need to come up with an algorithm, presumably involving other structures, that accomplishes the task. Problem 3. Using maps (Chapter 4, exercise 14, page 171) In May of 1844, Samuel F. B. Morse sent the message What hath God wrought! by telegraph from Washington to Baltimore, heralding the beginning of the age of electronic communication. To make it possible to communicate information using only the presence or absence of a single tone, Morse designed a coding system in which letters and other symbols are represented as coded sequences of short and long tones, traditionally called dots and dashes. In Morse code, the 26 letters of the alphabet are represented by the following codes: A B C D E F G H I J K L M N O P Q R S T U V W X Y Z

If you want to convert from letters to Morse code, you could use a switch statement or store the strings for each letter in a vector with 26 elements; to convert from Morse code to letters, the easiest approach is to use a map. Write a program that reads in lines from the user and translates each line either to or from Morse code depending on the first character of the line: If the line starts with a letter, you want to translate it to Morse code. Any characters other than the 26 letters should simply be ignored. If the line starts with a period (dot) or a hyphen (dash), it should be read as a series of Morse code characters that you need to translate back to letters. Each sequence of dots and dashes is separated by spaces, but any other characters should be ignored.

The program should end when the user enters a blank line. A sample run of this program (taken from the messages between the Titanic and the Carpathia in 1912) might look like this (note that there are no spaces in the Morse-to-letters translation):
Morse code translator > SOS TITANIC ... --- ... - .. - .- -. .. -.-. > WE ARE SINKING FAST .-- . .- .-. . ... .. -. -.- .. -. --. ..-. .- ... > .... . .- -.. .. -. --. ..-. --- .-. -.-- --- ..HEADINGFORYOU >

Problem 4. Using lexicons (Chapter 4, exercise 15, page 172) In Chapter 3, exercise 6, you were asked to write a function IsPalindrome that checks whether a word is a palindrome, which means that it reads identically forward and backward. Use that function together with the lexicon of English words to print out a list of all words in English that are palindromes.

w w
w

PD

PD

F-

XC

h a n g e Vi e

F-

XC

h a n g e Vi e

er

er

O W

bu

to

lic

lic

to

bu

N
.c

O W
w
.d o
c u -tr a c k

.d o

c u -tr a c k

.c

Eric Roberts CS106B

Handout #17A April 13, 2009

Solutions to Section Handout #2


Problem 1. Using grids
/* * Function: MakeGridOfCounts * Usage: MakeGridOfCounts(locations, counts); * ------------------------------------------* This function uses the first grid to indicate where mines are * located and creates a second grid showing the count of mines * in the neighborhood of each square. */ void MakeGridOfCounts(Grid<bool> & locations, Grid<int> & counts) { int nRows = locations.numRows(); int nCols = locations.numCols(); counts.resize(nRows, nCols); for (int i = 0; i < nRows; i++) { for (int j = 0; j < nCols; j++) { counts[i][j] = CountBombNeighbors(locations, i, j); } } } /* * Function: CountBombNeighbors * Usage: int nBombs = CountBombNeighbors(locations, row, col); * -----------------------------------------------------------* Counts the number of bombs in the immediate neighborhood. */ int CountBombNeighbors(Grid<bool> & locations, int row, int col) { int nBombs = 0; for (int i = -1; i <= 1; i++) { for (int j = -1; j <= 1; j++) { if (InGridBounds(locations, row + i, col + j)) { if (locations[row + i][col + j]) nBombs++; } } } return nBombs; } /* * Function: InGridBounds * Usage: if (InGridBounds(grid, row, col)) . . . * ---------------------------------------------* Returns true if [row][col] is inside the grid. */ bool InGridBounds(Grid<bool> & grid, int row, int col) { return row >= 0 && row < grid.numRows() && col >= 0 && col < grid.numCols(); }

w w
w

w
w

PD

PD

F-

XC

h a n g e Vi e

F-

XC

h a n g e Vi e

er

er

O W

bu

to

lic

lic

to

bu

N
.c

O W
w
.d o
c u -tr a c k

.d o

c u -tr a c k

.c

Problem 2. Using queues


/* * Reverses the order of elements in a queue using a stack. */ void ReverseQueue(Queue<string> & queue) { Stack<string> stack; while (!queue.isEmpty()) { stack.push(queue.dequeue()); } while (!stack.isEmpty()) { queue.enqueue(stack.pop()); } }

Problem 3. Using maps This problem was slightly buggy because there is no way to add spaces between the words when translating from Morse to English. The last lines of the sample run should look like this:
> .... . .- -.. .. -. --. ..-. --- .-. -.-- --- ..HEADINGFORYOU >

/* * File: MorseCode.cpp * -----------------* This program translates to and from Morse Code. * The translation from letters to Morse Code uses an array; * The translation from Morse Code to letters uses a map. */ #include #include #include #include #include "genlib.h" "simpio.h" "map.h" "strutils.h" <iostream>

/* Constants */ const string MORSE_CODE[] = { ".-" /* A */, "-..." "-.." /* D */, "." "--." /* G */, "...." ".---" /* J */, "-.-" "--" /* M */, "-." ".--." /* P */, "--.-" "..." /* S */, "-" "...-" /* V */, ".--" "-.--" /* Y */, "--.." }; /* Function protoypes */ string TranslateLettersToMorse(string line); string TranslateMorseToLetters(string line, Map<char> & morseTable); void CreateMorseCodeTable(Map<char> & morseTable); /* /* /* /* /* /* /* /* /* B E H K N Q T W Z */, */, */, */, */, */, */, */, */ "-.-." "..-." ".." ".-.." "---" ".-." "..-" "-..-" /* /* /* /* /* /* /* /* C F I L O R U X */, */, */, */, */, */, */, */,

w w
w

w
w

PD

PD

F-

XC

h a n g e Vi e

F-

XC

h a n g e Vi e

er

er

O W

bu

to

lic

lic

to

bu

N
.c

O W
w
.d o
c u -tr a c k

.d o

c u -tr a c k

.c

/* Main program */ int main() { cout << "Morse code translator" << endl; Map<char> morseTable; CreateMorseCodeTable(morseTable); while (true) { cout << "> "; string line = ConvertToUpperCase(GetLine()); if (line == "") break; if (line[0] == '.' || line[0] == '-') { cout << TranslateMorseToLetters(line, morseTable) << endl; } else { cout << TranslateLettersToMorse(line) << endl; } } return 0; } /* * Translates a string of letters into Morse Code by looking * up the letters in the MORSE_CODE array. */ string TranslateLettersToMorse(string line) { string morse = ""; for (int i = 0; i < line.length(); i++) { char ch = line[i]; if (ch >= 'A' && ch <= 'Z') { if (morse != "") morse += " "; morse += MORSE_CODE[ch - 'A']; } } return morse; } /* * Translates a string in Morse Code into letters using morseTable. * To eliminate the special case of not having a separator at the * end, this code simply makes sure one is there. */ string TranslateMorseToLetters(string line, Map<char> & morseTable) { line += " "; int start = 0; string letters = ""; for (int i = 0; i < line.length(); i++) { char ch = line[i]; if (ch != '.' && ch != '-') { if (start < i) { string morse = line.substr(start, i - start); if (morseTable.containsKey(morse)) { letters += morseTable[morse]; } else { letters += '?'; } } start = i + 1; } } return letters; }

w w
w

w
w

PD

PD

F-

XC

h a n g e Vi e

F-

XC

h a n g e Vi e

er

er

O W

bu

to

lic

lic

to

bu

N
.c

O W
w
.d o
c u -tr a c k

.d o

c u -tr a c k

.c

/* * Initializes the Morse Code map from the table. */ void CreateMorseCodeTable(Map<char> & morseTable) { for (char ch = 'A'; ch <= 'Z'; ch++) { morseTable[MORSE_CODE[ch - 'A']] = ch; } }

Problem 4. Using lexicons


/* * This program lists all palindromes in the English dictionary. */ int main() { cout << "This program finds all palindromes." << endl; Lexicon dictionary("EnglishWords.dat"); Lexicon::Iterator iter = dictionary.iterator(); while (iter.hasNext()) { string word = iter.next(); if (word == ReverseString(word)) { cout << word << endl; } } return 0; } /* * Function: ReverseString * Usage: string rev = ReverseString(str); * --------------------------------------* Returns a copy of str with the letters reversed. */ string ReverseString(string str) { string rev = ""; for (int i = str.length() - 1; i >= 0; i--) { rev += str[i]; } return rev; }

w w
w

w
w

PD

PD

F-

XC

h a n g e Vi e

F-

XC

h a n g e Vi e

er

er

O W

bu

to

lic

lic

to

bu

N
.c

O W
w
.d o
c u -tr a c k

.d o

c u -tr a c k

.c

CS106B Spring 2010

Handout 15

Section Handout

April 12th, 2010

Problem 1: URL Parameter Map A URL has many components. a protocol, a domain, and a file path are almost always included. Sometimes the URL includes a query, which the portion of the URL that appears after the ? and (if present) before the # (which defines the URLs hash). Here are some examples: http://www.google.com/ig?hl=en&source=iglk http://www.facebook.com/profile.php?id=1160&viewas=214707 http://store.apple.com/us/browse/family/iphone?mco=MTAyNTM5ODU

And while you may recognize the ? as a common character within URLs, you may not realize that the part following the ? is the serialization of a Map<string>. The query string is an &delimited string of key-value pairs, where each key-value pair takes the form <key>=<value>. When a web server gets an HTTP request, it digests the query string and re-hydrates it into Map<string> form, and uses that map to programmatically shape how the response page is generated. Thats why www.google.com/ig?hl=en&source=iglk generates English, and www.google.com/ig?hl=fr&source=iglk generates French. One little wrinkle: the value is technically optional, so that query strings like type=5&seeall and source_id=9074&read= are legitimate. When a key is present without a value, then the value is arbitrary and the presence of the key is the only thing of interest. In such cases, the Map<string> should include the key and attach an arbitrary value to it. Write a function that takes a legitimate URL, extracts the query string, and returns a Map<string> with all of its key-value pairs. You can safely assume that the URL is properly formatted, and that the structure of an URL is no more complicated than whats described here. Make sure you deal with URLs that dont include a query string, and URLs that include a hash.
Map<string> extractQueryMap(string url);

This problem is as much about string manipulation as it is about the Map, so dont be surprised if youre using some advanced string methods that havent come up in any previous examples. Problem 2: Publishing Stories Social networking sites like Facebook, LinkedIn, Orkut, Friendster, and MySpace typically record and publish stories about actions taken by you and your friends. Stories such as:

w w
w

w
w

PD

PD

F-

XC

h a n g e Vi e

F-

XC

h a n g e Vi e

er

er

O W

bu

2
w
w
o
.c

to

lic

lic

to

bu

N
w
.d o
c u -tr a c k

O W

.d o

c u -tr a c k

.c

John Dixon accepted your friend request. Jeff Barbose is no longer in a relationship. Scott James wrote a note called "The Two Percent Solution". Arlene Heitner commented on Melodie Bowshers video. Antonio Melara gave The French Laundry a 5-star review. are created from story templates like {name} accepted your friend request. {name} is no longer in a relationship. {name} wrote a note called "{title}". {name} commented on {target}s video. {actor} gave {restaurant} a {rating}-star review. The specific story is generated from the skeletal one by replacing the tokenssubstrings like "{name}", "{title}", and "{rating}"with event-specific values, like "John Dixon", "The Two Percent Solution", and "5". The token-value pairs can be packaged in a Map<string>, and given a story template and a data map, its possible to generate an actual story. Write the SubstituteTokens function, which accepts a story template (like "{actor} gave {restaurant} a {rating}-star review.") and a Map<string> (which might map "actor" to "Antonio Melara", "restaurant" to "The French Laundry", and "rating" to "5"), and builds a string just like the story template, except that the tokens have been replaced by the text they map to. Assume the following is true: '{' and '}' exist to delimit token names, but wont appear anywhere else. In other words, if you encounter the '{' character, you can assume it marks the beginning of a token that ends with a '}'. We guarantee that all tokens are in the Map<string>. You dont need to do any error checking. Note that the '{' and the '}' arent included in the map. "{name}", for instance, is replaced with whatever "name" identifies in the map. The prototype is:
string SubstituteTokens(string storyTemplate, Map<string>& data);

w w
w

PD

PD

F-

XC

h a n g e Vi e

F-

XC

h a n g e Vi e

er

er

O W

bu

3
w
w
o
.c

to

lic

lic

to

bu

N
w
.d o
c u -tr a c k

O W

.d o

c u -tr a c k

.c

Problem 3: Happy Numbers Starting with any positive integer n, replace n with the sum of the squares of its digits, and repeat the process until the number equals 1, or until you arrive at a previously arrived at number (and have thus entered a cycle). Numbers eventually leading to 1 are called happy numbers, and the rest are called unhappy, otherwise know as sad, numbers. Using a Set<int>, write the IsHappy predicate function, which accepts a positive number and returns true if and only if the number is happy, and false otherwise.
bool IsHappy(int n);

Problem 4: Chain Reactions Chain Reaction is a popular one-player game making its way through the Internet and various mobile platforms. In our version, weve given a collection of locations(x,y) coordinates in the planeof land mines. The detonation of any single land mine prompts all land mines within a certain distance to simultaneously detonate a second later, which themselves set off more land mines another second later, and so forth, and so forth. The chain reaction continues until there are no active land mines, or until none of the remaining land mines happen to fall within the threshold distance of those that have exploded. You get 0 points for the land mine you initially [and manually] detonate. You get 100 points for each land mine that detonates at the one-second mark. You get 400 points for each land mine that detonates at the two-second mark. You get 900 points for each land mine that detonates at the three-second mark. In general, you get 100n2 points for each land mine that detonates at the n-second mark.

Write the function called OptimalInitLocation, which returns the location of the land mine that should be detonated first if the goal is to maximize the overall score. If there are multiple initial detonations that lead to the same maximum score, then you can return any one of them. Refer to the following type definitions and helper functions:
struct location { double x; double y; }; int LocationCompare(location one, location two) { if (one.x == two.x) return OperatorCmp(one.y, two.y); return OperatorCmp(one.x, two.x); }

w w
w

PD

PD

F-

XC

h a n g e Vi e

F-

XC

h a n g e Vi e

er

er

O W

bu

4
w
w
o
.c

to

lic

lic

to

bu

N
w

O W
.d o
c u -tr a c k

.d o

c u -tr a c k

.c

static const double kThresholdDist = 1.5; bool IsInRange(location one, location two) { double deltax = one.x - two.x; double deltay = one.y - two.y; return sqrt(deltax * deltax + deltay * deltay) < kThresholdDist; } location OptimalInitLocation(Set<location>& landmines);

w w
w

PD

PD

F-

XC

h a n g e Vi e

F-

XC

h a n g e Vi e

er

er

O W

bu

to

lic

lic

to

bu

N
.c

O W
w
.d o
c u -tr a c k

.d o

c u -tr a c k

.c

CS106B Spring 2010

Handout 15S

Section Solution

April 14-21st, 2010

Solution 1: URL Parameter Map


Map<string> extractQueryMap(string url) { Map<string> parameters; int index = url.find('?'); if (index == string::npos) return parameters; string query = url.substr(index + 1); index = query.find('#'); if (index != string::npos) { query = query.substr(0, index); // chop off hash } Vector<string> pairs = Explode(query, '&'); for (int i = 0; i < pairs.size(); i++) { Vector<string> components = Explode(pairs[i], '='); string key = components[0]; string value = "true"; if (components.size() > 1 && !components[1].empty()) { value = components[1]; } parameters[key] = value; } return parameters; }

Solution 2: Publishing Stories Theres the one-pass approach that just appends characters from the template to the running story, unless that character is an '{', in which case we extract everything up through the matching '}' and substitute it with a string from the data map.
string SubstituteTokens(string storyTemplate, Map<string>& data) { string story; for (int i = 0; i < storyTemplate.size(); i++) { if (storyTemplate[i] != '{') { story += storyTemplate[i]; } else { int j = storyTemplate.find('}', i + 1); string token = storyTemplate.substr(i + 1, j - i - 1); story += data.get(token); i = j; } } return story; }

w w
w

w
w

PD

PD

F-

XC

h a n g e Vi e

F-

XC

h a n g e Vi e

er

er

O W

bu

2
w
w
o
.c

to

lic

lic

to

bu

N
w

O W
.d o
c u -tr a c k

.d o

c u -tr a c k

.c

Another approach is to iterate over the data map and drive the substitution that way. Its less efficient, but its another way to think about the problem and is a perfectly acceptable answer for the purposes of a discussion section.
string SubstituteOneToken(string story, string token, string value) { int start = 0; while (true) { int found = story.find(token); if (found == string::npos) return story; story.replace(found, token.size(), value); start = found + value.size() + 1; } } string SubstituteTokens(string storyTemplate, Map<string>& data) { string story = storyTemplate; foreach (string token in data) { story = SubstituteOneToken(story, '{' + token + '}', data.get(token)); } return story; }

Solution 3: Happy Numbers


int ComputeNext(int num) { int sumOfSquares = 0; while (num > 0) { int digit = num % 10; sumOfSquares += digit * digit; num /= 10; } return sumOfSquares; } bool IsHappy(int num) { Set<int> previouslySeen; while (num > 1 && !previouslySeen.contains(num)) { previouslySeen.add(num); num = ComputeNext(num); } return num == 1; }

w w
w

PD

PD

F-

XC

h a n g e Vi e

F-

XC

h a n g e Vi e

er

er

O W

bu

3
w
w
o
.c

to

lic

lic

to

bu

N
w

O W
.d o
c u -tr a c k

.d o

c u -tr a c k

.c

Solution 4: Chain Reactions As with all nontrivial problems, there are several reasonable approaches. But because you needed to compute what subset of the land mines exploded at second 1, then second 2, etc, some variation of breadth first search is almost certainly required.
int ComputeScore(location init, Set<location>& landmines) { int score = 0; int second = 0; Set<location> notYetExploded = landmines; notYetExploded.remove(init); Set<location> currentlyExploding(LocationCompare); currentlyExploding.add(init); while (!currentlyExploding.isEmpty()) { score += 100 * currentlyExploding.size() * second * second; Set<location> soonExploding(LocationCompare); foreach (location explodingLocation in currentlyExploding) { foreach (location potentialLocation in notYetExploded) { if (IsInRange(explodingLocation, potentialLocation)) { soonExploding.add(potentialLocation); } } } notYetExploded.subtract(soonExploding); currentlyExploding = soonExploding; second++; } return score; } location OptimalInitLocation(Set<location>& landmines) { int bestScore = -1; location bestLocation; foreach (location mine in landmines) { int score = ComputeScore(mine, landmines); if (score > bestScore) { bestScore = score; bestLocation = mine; } } return bestLocation; }

w w
w

PD

PD

F-

XC

h a n g e Vi e

F-

XC

h a n g e Vi e

er

er

O W

bu

to

lic

lic

to

bu

N
.c

O W
w
.d o
c u -tr a c k

.d o

c u -tr a c k

.c

Steve Cooper CS 106B

Handout #13 April 11, 2011

Section Handout #2ADTs


Parts of this handout were written by Julie Zelenski and Eric Roberts.

Problem 1. Using grids In the game of Minesweeper, a player searches for hidden bombs on a rectangular grid. One way to represent that grid in C++ is to use a grid of Boolean values marking bomb locations. A grid value is true if there is bomb at that location, false otherwise. Here is an example grid:
(0,0) T F T T F F F F T F F F F F F F T F F F T F F F F F F F F F T T T F F F

Given such a grid of bomb locations, write a function


void MakeGridOfCounts(Grid<bool> & locations, Grid<int> & counts);

that constructs a grid of integers storing the count of bombs in each neighborhood. The neighborhood of a location includes the location itself and the eight adjacent locations, but only if they are inside the boundaries of the grid. The reference parameter counts is used to store the result; your job is to make sure that it has the same size as the locations grid and then to assign to each element an integer between 0 and 9. For example, if sampleBombLocations contains the Boolean grid shown earlier, the code
Grid<int> sampleBombCounts; MakeGridOfCounts(sampleBombLocations, sampleBombCounts);

should initialize sampleBombCounts as follows:


(0,0) 1 3 3 3 1 0 1 3 3 4 2 1 0 2 2 3 1 1 0 1 1 2 1 1 2 4 3 2 0 0 2 3 2 1 0 0

Problem 2. Using queues (Chapter 4, exercise 9, page 169) Bob Dylans 1963 song The Times They Are A-Changin contains the following lines, which are themselves paraphrased from Matthew 19:30:

w w
w

w
w

PD

PD

F-

XC

h a n g e Vi e

F-

XC

h a n g e Vi e

er

er

O W

bu

lic

lic

2
w
.c

to

to

bu

N
w

O W
.d o
c u -tr a c k

.d o

c u -tr a c k

.c

And the first one now Will later be last For the times they are a-changin In keeping with this revolutionary sentiment, write a function
void ReverseQueue(Queue<string> & queue);

that reverses the elements in the queue. Remember that you have no access to the internal representation of the queue and will need to come up with an algorithm, presumably involving other data structures, that accomplishes the task. Problem 3. Using maps (Chapter 4, exercise 14, page 170) In May of 1844, Samuel F. B. Morse sent the message What hath God wrought! by telegraph from Washington to Baltimore, heralding the beginning of the age of electronic communication. To make it possible to communicate information using only the presence or absence of a single tone, Morse designed a coding system in which letters and other symbols are represented as coded sequences of short and long tones, traditionally called dots and dashes. In Morse code, the 26 letters of the alphabet are represented by the following codes: A B C D E F G H I J K L M N O P Q R S T U V W X Y Z

If you want to convert from letters to Morse code, you could use a switch statement or store the strings for each letter in a vector with 26 elements; to convert from Morse code to letters, the easiest approach is to use a map. Write a program that reads in lines from the user and translates each line either to or from Morse code depending on the first character of the line: If the line starts with a letter, you want to translate it to Morse code. Any characters other than the 26 letters should simply be ignored. If the line starts with a period (dot) or a hyphen (dash), it should be read as a series of Morse code characters that you need to translate back to letters. Each sequence of dots and dashes is separated by spaces, but any other characters should be ignored.

The program should end when the user enters a blank line. A sample run of this program (taken from the messages between the Titanic and the Carpathia in 1912) might look like this (note that there are no spaces in the Morse-to-letters translation):

w w
w

PD

PD

F-

XC

h a n g e Vi e

F-

XC

h a n g e Vi e

er

er

O W

bu

lic

lic

3
w
.c

to

to

bu

N
w

O W
.d o
c u -tr a c k

.d o

c u -tr a c k

.c

Problem 4. Using lexicons (Chapter 4, exercise 15, page 171) In Chapter 3, exercise 6, you were asked to write a function IsPalindrome that checks whether a word is a palindrome, which means that it reads identically forward and backward. Use that function together with the lexicon of English words to print out a list of all words in English that are palindromes. Problem 5. Processing maps in alphabetical order (Chapter 4, exercise 16, page 171) In Chapter 4, we introduced the program wordfreq.cpp (see Figure 4-8) that outputted the number of times each word in a given data file appears in that file. As noted in the chapter, it is actually rather easy to change the wordfreq.cpp program so that the words appear in alphabetical order. The only thing you need to do is think creatively about the tools that you already have. Rewrite the function
void DisplayWordCounts(Map<int> & wordCounts);

so that it outputs a given map of words to word counts such that words are listed alphabetically.

w w
w

PD

PD

F-

XC

h a n g e Vi e

F-

XC

h a n g e Vi e

er

er

O W

bu

to

lic

lic

to

bu

N
.c

O W
w
.d o
c u -tr a c k

.d o

c u -tr a c k

.c

Steve Cooper CS106B

Handout #13A April 15, 2011

Solutions to Section Handout #2


Problem 1. Using grids
/* * Function: MakeGridOfCounts * Usage: MakeGridOfCounts(locations, counts); * ------------------------------------------* This function uses the first grid to indicate where mines are * located and creates a second grid showing the count of mines * in the neighborhood of each square. */ void MakeGridOfCounts(Grid<bool> & locations, Grid<int> & counts) { int nRows = locations.numRows(); int nCols = locations.numCols(); counts.resize(nRows, nCols); for (int i = 0; i < nRows; i++) { for (int j = 0; j < nCols; j++) { counts[i][j] = CountBombNeighbors(locations, i, j); } } } /* * Function: CountBombNeighbors * Usage: int nBombs = CountBombNeighbors(locations, row, col); * -----------------------------------------------------------* Counts the number of bombs in the immediate neighborhood. */ int CountBombNeighbors(Grid<bool> & locations, int row, int col) { int nBombs = 0; for (int i = -1; i <= 1; i++) { for (int j = -1; j <= 1; j++) { if (locations.inBounds(row + i, col + j)) { if (locations[row + i][col + j]) nBombs++; } } } return nBombs; }

w w
w

w
w

PD

PD

F-

XC

h a n g e Vi e

F-

XC

h a n g e Vi e

er

er

O W

bu

lic

lic

2
w
.c

to

to

bu

N
w

O W
.d o
c u -tr a c k

.d o

c u -tr a c k

.c

Problem 2. Using queues


/* * Function: ReverseQueue * Usage: ReverseQueue(queue); * --------------------------* Reverses the order of the elements in a queue. This * implementation does so by using a stack to hold the * values as they are being reversed. */ void ReverseQueue(Queue<string> & queue) { Stack<string> stack; while (!queue.isEmpty()) { stack.push(queue.dequeue()); } while (!stack.isEmpty()) { queue.enqueue(stack.pop()); } }

Problem 3. Using maps


/* * File: MorseCode.cpp * ------------------* This program translates to and from Morse Code using maps to * assist in the translation. This implementation is actually * more general and would support translation back and forth * from single characters to multicharacter encodings. */ #include #include #include #include #include "genlib.h" "simpio.h" "map.h" "strutils.h" <iostream>

/* Function prototypes */ string TranslateLettersToTokens(string line, Map<string> & map); string TranslateTokensToLetters(string line, Map<string> & map); Map<string> CreateMorseCodeMap(); Map<string> InvertMap(Map< InvertMap(Map<string> & map);

w w
w

PD

PD

F-

XC

h a n g e Vi e

F-

XC

h a n g e Vi e

er

er

O W

bu

lic

lic

3
w
.c

to

to

bu

N
w

O W
.d o
c u -tr a c k

.d o

c u -tr a c k

.c

int main() { Map<string> lettersToMorse = CreateMorseCodeMap(); Map<string> morseToLetters = InvertMap(lettersToMorse); cout << "Morse code translator" << endl; while (true) { cout << "> "; string line = ConvertToUpperCase(GetLine()); if (line == "") break; if (line[0] == '.' || line[0] == ' '-') { cout << TranslateTokensToLetters TranslateTokensToLetters(line, morseToLetters) << endl; } else { cout << TranslateLettersToTokens(line, lettersToMorse) << endl; } } return 0; } /* * Function: TranslateLettersToTokens * Usage: string morse = TranslateLettersToTokens(line, map); * ---------------------------------------------------------* Translates a string of letters into multi nslates multi-character tokens as * specified by the map. The translated tokens are separated by * spaces. Characters that don't appear in the map are simply * ignored. */ string TranslateLettersToTokens(string line, Map<string> & map) { Map<string> string morse = ""; for (int i = 0; i < line.length(); i++) { string letter = ConvertToUpperCase(line.substr(i, 1)); if (map.containsKey(letter)) { if (morse != "") morse += " "; morse += map[letter]; } } return morse; } /* * Function: TranslateTokensToLetters * Usage: string letters = TranslateLettersToTokens(line, map); * -----------------------------------------------------------* Translates a string consisting of coded letters separated by * spaces into the equivalent single character codes as specified single-character * by the map. If codes appear that are not included in the map, * this function indicates that fact by inserting a question mark. * * Implementation note: To eliminate the special case of the last * character in the line, this function begins by adding a space * to the end of the input string. */

w w
w

PD

PD

F-

XC

h a n g e Vi e

F-

XC

h a n g e Vi e

er

er

O W

bu

lic

lic

4
w
.c

to

to

bu

N
w

O W
.d o
c u -tr a c k

.d o

c u -tr a c k

.c

string TranslateTokensToLetters(string line, Map<string> & map) { line += " "; string letters = ""; string morse = ""; for (int i = 0; i < line.length(); i++) { char ch = line[i]; if (ch == '.' || ch == ' '-') { morse += ch; } else if (ch == ' ') { if (map.containsKey(morse)) { letters += map[morse]; } else { letters += '?'; } morse = ""; } } return letters; } /* * Function: CreateMorseCodeMap * Usage: Map<string> map = CreateMorseCodeMap(); * ---------------------------------------------* Returns a map in which each uppercase letter is mapped into its * Morse code equivalent. */ Map<string> CreateMorseCodeMap() { Map<string> map; map["A"] = ".-"; map["B"] = "-..."; map["C"] = "-.-."; map["D"] = "-.."; map["E"] = "."; map["F"] = "..-."; map["G"] = "--."; map["H"] = "...."; map["I"] = ".."; map["J"] = ".---"; map["K"] = "-.-"; map["L"] = ".-.."; map["M"] = "--"; map["N"] = "-."; map["O"] = "---"; map["P"] = ".--."; map["Q"] = "--.-"; map["R"] = ".-."; map["S"] = "..."; map["T"] = "-"; map["U"] = "..-"; map["V"] = "...-"; map["W"] = ".--"; map["X"] = "-..-"; map["Y"] = "-.--"; map["Z"] = "--.."; return map; }

w w
w

PD

PD

F-

XC

h a n g e Vi e

F-

XC

h a n g e Vi e

er

er

O W

bu

lic

lic

5
w
.c

to

to

bu

N
w

O W
.d o
c u -tr a c k

.d o

c u -tr a c k

.c

/* * Function: InvertMap * Usage: Map<string> inverse = InvertMap(map); * -------------------------------------------* Creates an inverted copy of the specified map in which the values * in the original become the keys of the new map and refer back to * their associated keys. Thus, if "abc" is bound to "xyz" in the * original map, the inverted map will bind "xyz" to "abc". If two * keys in the original map have the same value, this function will * signal an error condition. */ Map<string> InvertMap(Map<string> & map) { Map<string> inverse; foreach (string key in map) { string value = map[key]; if (inverse.containsKey(value)) { Error("That map cannot be inverted"); } inverse[value] = key; } return inverse; }

Problem 4. Using lexicons


/* * This program lists all palindromes in the English dictionary. */ int main() { cout << "This program finds all palindromes." << endl; Lexicon english("EnglishWords.dat"); foreach (string word in english) { if (IsPalindrome(word) { IsPalindrome(word)) cout << word << endl; } } return 0; } /* * Function: IsPalindrome * Usage: if (IsPalindrome IsPalindrome(str)) ... * --------------------------------------* Returns true if str is a palindrome; false otherwise. */ bool IsPalindrome(string str) { (string string rev = ""; for (int i = str.length() - 1; i >= 0; i--) { rev += str[i]; } return rev == str; }

w w
w

PD

PD

F-

XC

h a n g e Vi e

F-

XC

h a n g e Vi e

er

er

O W

bu

lic

lic

6
w
.c

to

to

bu

N
w

O W
.d o
c u -tr a c k

.d o

c u -tr a c k

.c

Problem 5. Processing maps in alphabetical order


/* * Displays the count associated with each word in the frequency * table. This version uses a lexicon to ensure that the words * appear in alphabetical order. */ void DisplayWordCounts(Map<int> & wordCounts) { Lexicon words; foreach (string word in wordCounts) { words.add(word); } foreach (string word in words) { cout << left << setw(15) << word << right << setw(5) << wordCounts[word] << endl; } }

w w
w

PD

PD

F-

XC

h a n g e Vi e

F-

XC

h a n g e Vi e

er

er

O W

bu

to

lic

lic

to

bu

N
.c

O W
w
.d o
c u -tr a c k

.d o

c u -tr a c k

CS106B Spring 2012

Handout #10 April 16, 2012

.c

Section Handout 2
_________________________________________________________________________________________________________

This week's section handout is all about collections. There are two problems here one that probes the mysteries of the English language, and one that probes the mysteries of life itself. Problem One: Xzibit Words Some words contain other words as substrings. For example, the word pirates contains a huge number of words as substrings: a at ate ates es I irate pi pirate pirates rat rate rates

Note that pirates is a substring of itself. The word pat is not considered a substring of pirates, since even though all the letters of pat are present in pirate in the right order, they aren't adjacent to one another. Let's call a word an Xzibit word if it contains a large number of words as substrings. Write a function string mostXzibitWord(Lexicon& words); that accepts as input a Lexicon of all words in English, then returns the word with the largest number of words contained as substrings.

-1-

w w
w

w
w

PD

PD

F-

XC

h a n g e Vi e

F-

XC

h a n g e Vi e

er

er

O W

bu

to

lic

lic

to

bu

N
.c

O W
w
.d o
c u -tr a c k

.d o

c u -tr a c k

Problem Two: RNA Protein Codes

.c

RNA (ribonucleic acid) is a chemical that can encode genetic information. Plant and animal cells use RNA for a variety of cell functions, while viruses often use RNA as their primary genetic storage. Each strand of RNA consists of a series of base pairs adenine (A), guanine (G), uracil (U), and cytosine (C) and a strand of RNA can be thought of as a string over those four letters. Because of this, computational geneticists often treat DNA and RNA as strings and run algorithms on those strings to deduce their properties. RNA is used by a cell to encode proteins, biological molecules essential to cell function. Each protein consists of a series of amino acids that, strung together, serve some complex purpose. The actual letters in an RNA strand spell out what amino acids need to be combined together to produce the overall protein. So how exactly are these amino acids represented? In RNA, letters are grouped into clusters of three letters called codons. Each codon encodes a specific choice of amino acid. When decoding RNA, the cell reads these codons in sequence and assembles the protein one amino acid at a time. For example, the RNA strand GGGAUGAAUAUCUCGGCG would be treated at this sequence of three-letter codons: GGG AUG AAU AUC UCG GCG The cell would then use the codons to determine what amino acids to string together into a protein. In this case, these codons represent the following sequence of amino acids: GGG AUG AAU AUC UCG GCG Glycine Methionine Asparagine Isoleucine Serine Alanine So the generated protein would have amino acids ordered as glycine, then methionine, then aspargine, then isoleucine, then serine, and finally alanine. An important detail is that each strand of RNA does not encode just one protein; typically, a single strand of RNA encodes many different proteins. How, then, does the cell know where each protein starts and stops? There is an ingenious system set up for just this purpose. In an RNA strand, the codon AUG has two meanings it can code for methionine (as shown above), but it also acts as a special start of protein marker. As a cell scans across an RNA strand, if it encounters the codon AUG, it begins assembling a protein starting at that location. It then continues to assemble the protein one amino acid at a time by decoding codons in sequence. The cell stops assembling base pairs when it encounters one of three stop codons (UAA, UAG, or UGA) that act as an end-of-protein marker. The cell can then keep scanning across the RNA until it finds another AUG start codon, at which point the process repeats. For example, consider this RNA strand: GCAUGGAUUAAUAUGAGACGACUAAUAGGAUAGUUACAACCCUUAUGUCACCGCCUUGA This would be decoded as follows: GC AUGGAUUAA U AUGAGACGACUAAUAGGAUAG UUACAACCCUU AUGCACCGCCUUGA Skip letters until we find AUG. Read from AUG until we hit a stop codon (here, UAA) Skip letters until we find AUG. Read from AUG until we hit a stop codon (here, UAG) Skip letters until we find AUG. Read from AUG until we hit a stop codon (here, UGA)

-2-

w w
w

w
w

PD

PD

F-

XC

h a n g e Vi e

F-

XC

h a n g e Vi e

er

er

O W

bu

to

lic

lic

to

bu

N
.c

O W
w
.d o
c u -tr a c k

.d o

c u -tr a c k

Your job is to write a function


Vector<string> findProteins(string& rna, Map<string, string> codons);

.c

that accepts as input a string of RNA and returns a Vector of all the proteins that would be formed from that RNA. The first parameter represents the actual string of RNA, and the second parameter is a Map from three-letter codons to the name of the amino acid they represent (or the special string "stop" if the codon is a stop codon). For example, running this function on the above RNA strand would return the Vector holding the stings
methionine, asparitic acid methionine, arginine, arginine, leucine, isoleucine, glycine methionine, serine, proline, proline (encoded by AUGGAUUAA) (encoded by AUGAGACGACUAAUAGGAUAG) (encoded by AUGUCACCGCCUUGA)

You can assume that all the proteins are properly terminated, which means that if you find an AUG codon then there will be a matching stop codon before the end of the protein.

-3-

w w
w

w
w

PD

PD

F-

XC

h a n g e Vi e

F-

XC

h a n g e Vi e

er

er

O W

bu

to

lic

lic

to

bu

N
.c

O W
w
.d o
c u -tr a c k

.d o

c u -tr a c k

CS106B Spring 2012

Handout #10S April 16, 2012

.c

Section Solutions 2
_________________________________________________________________________________________________________

Problem One: Xzibit Words One possible implementation is shown here:


string mostXzibitWord(Lexicon& words) { /* Track the best string we've found so far and how many subwords it has. */ string result; int numSubwords = 0; foreach (string word in words) { /* Store all the subwords we find. To avoid double-counting * words, we'll hold this in a Lexicon. */ Lexicon ourSubwords; /* Consider all possible start positions. */ for (int start = 0; start < word.length(); start++) { /* Consider all possible end positions. Note that we include * the string length itself, since that way we can consider * substrings that terminate at the end of the string. for (int stop = start; stop <= word.length(); stop++) { /* Note the C++ way of getting a substring. */ string candidate = word.substr(start, stop start); /* As an optimization, if this isn't a prefix of any legal * word, then there's no point in continuing to extend this * substring. */ if (!words.containsPrefix(candidate)) break; /* If this is a word, then record it as a subword. */ if (words.contains(candidate)) ourSubwords.add(candidate); } }

/* Having found all subwords, see if this is better than our * best guess so far. */ if (numSubwords < ourSubwords.size()) { result = word; numSubwords = ourSubwords.size(); } } } return result;

In case you're curious, the most Xzibit word is foreshadowers, with 34 subwords!

-1-

w w
w

w
w

PD

PD

F-

XC

h a n g e Vi e

F-

XC

h a n g e Vi e

er

er

O W

bu

to

lic

lic

to

bu

N
.c

O W
w
.d o
c u -tr a c k

.d o

c u -tr a c k

Problem Two: RNA Protein Codes Here is one possible implementation:


Vector<string> findProteins(string rna, Map<string, string>& codons) { Vector<string> result; /* Track at which index we are in the string. We'll be going one character * at a time through the string. */ int index = 0; while (true) { /* Find the next start codon, stopping if none are left. */ index = rna.find("AUG", index); if (index == string::npos) { return result; } /* Keep decoding codons until we hit a stop codon. */ string protein; while (true) { /* Read the codon. */ string codon = rna.substr(index, 3); index += 3; /* If it's a stop codon, we're done with this protein. */ if (codons[codon] == "stop") break; /* Otherwise, add it to the result. To get the commas right, we'll * only add commas if the string isn't empty. */ if (!protein.empty()) protein += ", "; protein += codons[codon]; } /* Add this protein to the result. */ result += protein; } }

.c

A process similar to this one is actually going on right now in every single cell in your body. Isn't that amazing?

-2-

w w
w

w
w

PD

PD

F-

XC

h a n g e Vi e

F-

XC

h a n g e Vi e

er

er

O W

bu

to

lic

lic

to

bu

N
.c

O W
w
.d o
c u -tr a c k

.d o

c u -tr a c k

.c

CS106X Fall 02-03

Handout #14 October 7, 2002

Section Handout #2
Problem 1: Compiler, compiler, what do you want? You are working hard on your latest assignment and come up with the following code:
enum directionT { North = 0, East, South, West }; int main() { directionT dir = North; int num = 0; dir = num; num = dir; return 0; } /* One of these two*/ /* lines does not compile*/

One of the lines above doesnt compile. Which line is it? Why does one line work and the other one doesnt? How could you fix this problem? Problem 2: Removing all occurrences of a character Write a function RemoveAllOccurrences that takes a string as input and returns the string with a given character removed. Stanford University with the n removed becomes Staford Uiversity Llamas like to laugh with the l removed becomes Lamas ike to augh and so on . . . Use the following function prototype to get started:
static string RemoveAllOccurrences (string text, char ch);

Problem 3: Rot-N Now that youve learned the relationship between strings and characters, its time to take advantage of that knowledge and send secret messages to all of your friends. The simplest way of doing that is to use an algorithm called Rot-N, which as its name implies simply rotates the alphabet by an arbitrary number of characters. For example, if we choose to rotate by 13, A becomes N, b becomes o, Y becomes L, and so on. Characters that are not letters do not change, so + remains +, ; remains ;, etc. If we just rotated the lower case alphabet by one letter, this is what wed get:

w w
w

w
w

PD

PD

F-

XC

h a n g e Vi e

F-

XC

h a n g e Vi e

er

er

O W

bu

lic

lic

2
w
.c

to

to

bu

O W
w
.d o
c u -tr a c k

.d o

c u -tr a c k

We can take advantage of this wraparound of RotN that allows us to have a particularly simple algorithm for doing the rotation (hint: think about what you know in C++ that has the same behavior)
a b, b c, . . ., y z, z

.c

a.

Of course, no secret message is useful if the original message remains accessible, so our Rot-N function will change the given string in place, taking the string as its argument. Your function should have the following prototype:
void RotN(string& message, int shiftAmount);

Problem 4: Find and Replace Write a function to find all the occurrences of a search string in a larger string and then to replace them with a different string. You have two choices over the function prototype:
static string FindAndReplace(string text, string find, string replace); static void FindAndReplace(string& text, string find, string replace);

Discuss the merits of both prototypes and then write the solution for the prototype you choose. Problem 5: Simple file reading Write a function SumFromFile that takes a file name, opens the file and reads numbers out of a file until it reads a blank line. The numbers from the file should be added together and returned as the result of the function. Your prototype should look as follows:
int SumFromFile (string filename);

Remember that you can use functions from the strutils.h header file to help you out (like StringToInteger). Also, you can assume that the file is well formatted and that it contains only one number per line, ending with a totally blank line. Problem 6: Passing By Reference Below is a simple function trace, similar to problems you will see on your midterm and finals. Being able to trace functions and understand whats going on is an important skill to have, in addition to being useful for test-taking. This function trace looks at the role that references and strings play in C++ code. Draw a full picture of the stack and memory at the specified points. Also, what gets printed here? The code to trace is printed below:
void Confuser (string dunno, string& clueless) { dunno.insert(dunno.length() - 5, clueless); clueless = clueless.substr(0, 1) + 'h' + ' '; clueless += "no!";

Draw a picture of the memory at this point.

static string DunceCap (string dumb, string& dumber) { dumb = dumb + dumber; dumber = dumber.substr (7, 5);

Draw a picture of the memory at this point.

w w
w

PD

PD

F-

XC

h a n g e Vi e

F-

XC

h a n g e Vi e

er

er

O W

bu

lic

lic

3
w
.c

to

to

bu

N
w

O W
.d o
c u -tr a c k

.d o

c u -tr a c k

.c

return dumb[4] + (dumb.at(16) + dumber); } int main () { string quest, tiz; quest = "What's going here?"; tiz = "on "; Confuser (quest, tiz); cout << DunceCap(tiz, quest); return 0; }

Problem 7: Arrays a la Mode The text discusses finding the mean and the median of an array of integers. The mode of a set of numbers is simply the value that occurs most often. For example, in the array 43
0

65
1

72
2

75
3

79
4

82
5

82
6

84
7

84
8

84
9

86
10

90
11

94
12

the mean (or average) is the sum of the numbers divided by the number of entries (1020/13 or 78), the median is the value in the midpoint element (array[6] or 82), and the mode is the value which occurs most frequently (in this case, 84). Write a function Mode with the following prototype:
int Mode(int array[], int size)

The function takes an integer array and the actual number of elements in the array, and returns the mode of the array. If there is more than one mode (which happens, for example, if two or more different values each occur more frequently than other values but themselves occur the same number of times), your program may return any of those values as the mode. Your program may not assume that the array is initially sorted, but you may sort it as part of the implementation of Mode by calling the Sort routine. If you get this right away, try to think of a different approach that will do the same thing.

w w
w

PD

PD

F-

XC

h a n g e Vi e

F-

XC

h a n g e Vi e

er

er

O W

bu

to

lic

lic

to

bu

N
.c

O W
w
.d o
c u -tr a c k

.d o

c u -tr a c k

.c

CS106X Fall 02-03

Handout #14S October 8, 2002

Section Solutions #2
Problem 1: Compiler, Compiler, what do you want? The line that will not compile in this example is the line that performs the assignment dir = num;. Why not? We talked in class about how enums are represented at run-time as integers, but that at compile time, the C++ compiler provides addition type checking information to make your code safer. In this case, since num is in integer type, the compiler will not let you assign an integer to an enumeration. Could this be fixed? Of course! All we need to do is to add in a typecast so that the compiler gets convinced that you know what you are doing. The following line does the same thing as the line above, but will actually compile:
dir = directionT(num);

Problem 2: Removing all occurrences of a character If we want to remove the occurrences of the letter one at a time, we can write the following function:
/* Function: RemoveAllOccurrences * Usage: s = RemoveAllOccurrences(input, ch); * -----------------------------------* This function takes a string and returns a * corresponding string with all the * occurrences of a given letter removed. It uses a for-loop * to iterate through the string testing each * character to see if it matches the letter to remove, * and building the string character by character. */ static string RemoveAllOccurrences (string text, char ch) { string result = ""; for (int i = 0; i < text.length(); i++) { if (text[i] != ch) { result += text[i]; } } return result; }

w w
w

w
w

PD

PD

F-

XC

h a n g e Vi e

F-

XC

h a n g e Vi e

er

er

O W

bu

lic

lic

2
w
.c

to

to

bu

N
w

O W
.d o
c u -tr a c k

.d o

c u -tr a c k

.c

We can also do the same thing by using the .find and .substr methods from the string class:
static string RemoveAllOccurrences (string text, char ch) { int pos; string result = text; while (true) { pos = result.find(ch); if (pos == string::npos) { break; } else { result = result.substr(0, pos) + result.substr(pos + 1, result.length() - pos - 1); } } return result; }

Problem 3: RotN There are many different ways of writing the Rot13 function itself. The one listed below works reasonably well, but most anything with the same result is equally good or better.
#define ALPHA_LENGTH 26

static void RotN(string& message, int shiftAmount) { char currentCh; for (int i = 0; i < message.length(); i++) { currentCh = message[i]; if (isalpha(currentCh)) { if (isupper(currentCh)) { currentCh = (((currentCh - 'A') + shiftAmount) % ALPHA_LENGTH) + 'A'; } else { currentCh = (((currentCh - 'a') + shiftAmount) % ALPHA_LENGTH) + 'a'; } message[i] = currentCh; } } }

w w
w

PD

PD

F-

XC

h a n g e Vi e

F-

XC

h a n g e Vi e

er

er

O W

bu

lic

lic

3
w
.c

to

to

bu

N
w

O W
.d o
c u -tr a c k

.d o

c u -tr a c k

.c

Problem 4: Find and Replace Both prototypes of the FindAndReplace function have their benefits: static string FindAndReplace(string text, string find, string replace); will create a new copy of the string you are working on, so that both strings will be available for later manipulation. However, most normal use only requires the modified version of the string. If we use the second prototype static void FindAndReplace(string& text, string find, string replace);, we dont have to worry about confusing the modified and unmodified strings. All we get is the modified string, which might make for more understandable code. So, we will prefer the second prototype of FindAndReplace. Now, we have to implement the function, as shown below:
static void FindAndReplace(string& text, string find, string replace) { int findPos; int startPos = 0; while (true) { findPos = text.find(find, startPos); if (findPos == string::npos) break; text = text.replace (findPos, find.length(), replace); //this line serves to insure that we don't get stuck in //an infinite loop when we replace the strings startPos = findPos + replace.length(); } }

Problem 5: SumFromFile
#include <iostream> #include <fstream> #include "strutils.h" int SumFromFile (string filename) { int sum = 0; ifstream in; int num; in.open(filename.c_str()); //check to make sure the ifstream is valid if (in.fail()) return sum; while (!in.fail()) { in >> num; sum += num; } in.close(); return sum; }

w w
w

PD

PD

F-

XC

h a n g e Vi e

F-

XC

h a n g e Vi e

er

er

O W

bu

lic

lic

4
w
.c

to

to

bu

N
w

O W
.d o
c u -tr a c k

.d o

c u -tr a c k

.c

Problem 6: Passing By Reference The first time we draw the memory for the trace, it looks like this:
main quest tiz Confuser dunno clueless Whats going on here? Whats going here? oh no!

The second time we draw the memory, it looks like:


main quest tiz DunceCap dumb dumber oh no!Whats going here? going oh no!

Finally, the console prints:


ongoing

Problem 7: Arrays a la Mode As with many algorithmic problems, there are many ways to accomplish the task o finding a mode. Probably the most straightforward mechanism is to simply count the times each element appears:
/* Helper function declarations */ static int CountElement(int value, int array[], int size); /* * Function: Mode * -------------* This implementation just goes through the array and counts the number of * times each element appears, keeping track of the current maximum value. */ static int Mode(int array[], int size) { int mode, max, count; max = 0; for (int i = 0; i < size; i++) { count = CountElement(array[i], array, size); if (count > max) {

w w
w

PD

PD

F-

XC

h a n g e Vi e

F-

XC

h a n g e Vi e

er

er

O W

bu

lic

lic

5
w
.c

to

to

bu

N
w

O W
.d o
c u -tr a c k

.d o

c u -tr a c k

.c

max = count; mode = array[i]; } } return (mode); } /* * Function: CountElement * ---------------------* Returns the number of times the first argument appears in the array. */ static int CountElement(int value, int array[], int size) { int count; count = 0; for (int i = 0; i < size; i++) { if (value == array[i]) { count++; } } return (count); }

Alternatively, one could sort the array first and then just check the length of each run of consecutive elements. This is slightly more complicated, but certainly more efficient, as shown in this version of Mode (which assumes that we've already defined a standard Sort function):
static int Mode(int array[], int size) { int start, count, max, mode; //Sort(array, size); start = max = 0; count = 1; for (int i = 1; i < size; i++) { if (array[i] == array[start]) { count++; } else { if (count > max) { mode = array[start]; max = count; } start = i; count = 1; } } if (count > max) { mode = array[start]; // Takes care of the last run. } return (mode); }

w w
w

PD

PD

F-

XC

h a n g e Vi e

F-

XC

h a n g e Vi e

er

er

O W

bu

to

lic

lic

to

bu

N
.c

O W
w
.d o
c u -tr a c k

.d o

c u -tr a c k

.c

Mehran Sahami CS106X

Handout #17 October 11, 2004

Section Handout #2
Problem 1: Simple file reading Write a function SumFromFile that takes a file name, opens the file and reads numbers out of a file until it reads a blank line. The numbers from the file should be added together and returned as the result of the function. Your prototype should look as follows:
int SumFromFile (string filename);

Remember that you can use functions from the strutils.h header file to help you out (like StringToInteger). Also, you can assume that the file is well formatted and that it contains only one number per line, ending with a totally blank line.

Problem 2: Arrays a la Mode The text discusses finding the mean and the median of an array of integers. The mode of a set of numbers is simply the value that occurs most often. For example, in the array below 43
0

65
1

72
2

75
3

79
4

82
5

82
6

84
7

84
8

84
9

86
10

90
11

94
12

the mean (or average) is the sum of the numbers divided by the number of entries (1020/13 or 78), the median is the value in the midpoint element (array[6] or 82), and the mode is the value occurring most frequently (in this case, 84). Write a function Mode with the following prototype:
int Mode(int array[], int size)

The function takes an integer array and the number of elements in the array, and returns the mode of the array. If there is more than one mode (which happens, for example, if two or more different values each occur more frequently than other values but occur the same number of times as each other), your program may return any of those values as the mode. Your program may not assume that the array is initially sorted, but you may sort it as part of the implementation of Mode by calling the Sort routine. If you get this right away, try to think of a different approach that will do the same thing.

w w
w

w
w

PD

PD

F-

XC

h a n g e Vi e

F-

XC

h a n g e Vi e

er

er

O W

bu

lic

lic

2
w
.c

to

to

bu

N
w

O W
.d o
c u -tr a c k

.d o

c u -tr a c k

.c

Problem 3: Quick Pointer Questions. Take this crash course test on pointers and memory: which of the following would cause compile-time or run-time problems? Also, what types are the left and right hand sides of each expression?
double x, *px, a[5]; x = *px; *px = x; px = &x; &x = px; &(x+1) = x; &x + 1 = x; *(&(x+1)) = x; *(&(x)+1) = x; x = a; x = a[0]; x = *(a[1]); x = (*a)[2]; x = a[3+1]; x = a[3] +1; x = &((a[3])+1); x = &(a[3]) +1; x = *(&(a[3])+1); px = a; px = a[0]; px = &(a[4]); /* /* /* /* /* /* /* /* /* /* /* /* /* /* /* /* /* /* /* /* 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 */ */ */ */ */ */ */ */ */ */ */ */ */ */ */ */ */ */ */ */

w w
w

PD

PD

F-

XC

h a n g e Vi e

F-

XC

h a n g e Vi e

er

er

O W

bu

lic

lic

3
w
.c

to

to

bu

N
w

O W
.d o
c u -tr a c k

.d o

c u -tr a c k

.c

Problem 4: Function Trace Trace through the execution of the following program and show the output it produces. Also, draw the state of memory at the point indicated.
/* * File: nonsense.cpp * This program exists only to test your understanding of pointers. */ #include <iostream> #include "genlib.h" static int Mystery(int *y, int x); static void SlapStick(int *x, int y, int *z); int main() { int x, y, z; x = 5; y = 6; z = Mystery(&x, y); cout << "Main: x = " << x << ", y = " << y << ", z = " << z << endl; SlapStick(&z, y, &y); cout << "Main: x = " << x << ", y = " << y << ", z = " << z << endl; return 0; } static int Mystery(int *y, int x) { *y = x; x += *y; cout << "Mystery: x = " << x << ", *y = " << *y << endl;

Draw a diagram indicating the contents of memory at this point


return (x); } static void SlapStick(int *x, int y, int *z) { int *p1; y = Mystery(z, *x); p1 = z; z = x; x = p1; *p1 = y + *z/2; cout << "SlapStick: *x = " << *x << ", y = " << y; cout << ", *z = " << *z << ", *p1 = " << *p1 << endl; }

w w
w

PD

PD

F-

XC

h a n g e Vi e

F-

XC

h a n g e Vi e

er

er

O W

bu

lic

lic

4
w
.c

to

to

bu

N
w

O W
.d o
c u -tr a c k

.d o

c u -tr a c k

.c

Problem 5: Memory Diagram Draw a diagram showing the contents of memory at the indicated point. Be sure to differentiate between the stack and the heap.
static void HousingDraw(double **flomo, int lag, double wilbur[], int* & roble) { int* stern; int** govCo; stern = new int[2]; govCo = &stern; stern[0] = sizeof(stern) / 3; stern[1] = stern[0] * 3; *flomo = new double[lag]; roble = new int[2]; wilbur++; *(wilbur + 1) = 4; for(lag = 0; lag < *wilbur; lag++) { wilbur[lag] = wilbur[lag] / 2; } roble = *govCo; }

int main() { int* toyon; double *kimball, branner[3]; for(int i = 0; i < 3; i++) {

Draw a diagram indicating the contents of memory at this point


branner[i] = 6 / (i + 1); } HousingDraw(&kimball, branner[2], branner, toyon); return 0; }

w w
w

PD

PD

F-

XC

h a n g e Vi e

F-

XC

h a n g e Vi e

er

er

O W

bu

lic

lic

5
w
.c

to

to

bu

N
w

O W
.d o
c u -tr a c k

.d o

c u -tr a c k

.c

Problem 6: 2-D Arrays Write a function Make2D that takes a one-dimensional array, a row size, and a column size, and returns a 2-D array from the numbers in the first array. Make sure that your function can deal with arrays of different dimensions. You can also assume that there will be enough data in the array to fill the 2-D array of the specified size (i.e. there will be at least row * col elements in the one-dimensional array) For example, if we provide the following array:
3.7 8.2 4.0 9.5 2.7 6.4 9.8 5.4 9.1 3.5 5.6 7.8 8.7 2.0 9.4 7.1

And the row and column dimensions 4 and 4, we should get a pointer to a dynamically allocated array that looks like this:

Heap
2.7 6.4 9.8 5.4

3.7 8.2 4.0 9.5

9.1 3.5 5.6

8.7 7.8 2.0 9.4 7.1

The prototype is as follows:


static double** Make2D(double array[], int rows, int cols);

w w
w

PD

PD

F-

XC

h a n g e Vi e

F-

XC

h a n g e Vi e

er

er

O W

bu

to

lic

lic

to

bu

N
.c

O W
w
.d o
c u -tr a c k

.d o

c u -tr a c k

.c

Mehran Sahami CS106X

Handout #17S October 11, 2004

Section Solutions #2
Problem 1: SumFromFile
#include <iostream> #include <fstream> #include "strutils.h" int SumFromFile(string filename) { int sum = 0, num; ifstream in; in.open(filename.c_str()); // check to make sure the ifstream is valid if (in.fail()) return sum; string line; // read from file until we reach a blank line or end-of-file while (true) { getline(in, line); if (in.fail() || line == "") break; num = StringToInteger(line); sum += num; } in.close(); return sum; }

Problem 2: Arrays a la Mode As with many algorithmic problems, there are many ways to accomplish the task of finding a mode. Probably the most straightforward mechanism is to simply count the times each element appears, as exemplified in the code below:
/* * Function: CountElement * ---------------------* Returns the number of times that "value" appears in the array. */ static int CountElement(int value, int array[], int size) { int count; count = 0; for (int i = 0; i < size; i++) { if (value == array[i]) { count++; }

w w
w

w
w

PD

PD

F-

XC

h a n g e Vi e

F-

XC

h a n g e Vi e

er

er

O W

bu

lic

lic

2
w
.c

to

to

bu

N
w

O W
.d o
c u -tr a c k

.d o

c u -tr a c k

.c

} return (count); }

w w
w

PD

PD

F-

XC

h a n g e Vi e

F-

XC

h a n g e Vi e

er

er

O W

bu

lic

lic

3
w
.c

to

to

bu

N
w

O W
.d o
c u -tr a c k

.d o

c u -tr a c k

.c

/* * Function: Mode * -------------* This implementation just goes through the array and counts the number of * times each element appears, keeping track of the current maximum value. */ static int Mode(int array[], int size) { int mode, max, count; max = 0; for (int i = 0; i < size; i++) { count = CountElement(array[i], array, size); if (count > max) { max = count; mode = array[i]; } } return (mode); }

Alternatively, one could sort the array first and then just check the length of each run of consecutive elements. This is slightly more complicated, but certainly more efficient, as shown in this version of Mode (which assumes that we've already defined a standard Sort function):
static int Mode(int array[], int size) { int start, count, max, mode; Sort(array, size); // Assume we have this function which sorts an array start = max = 0; count = 1; for (int i = 1; i < size; i++) { if (array[i] == array[start]) { count++; } else { if (count > max) { mode = array[start]; max = count; } start = i; count = 1; } } if (count > max) { mode = array[start]; // Takes care of the last run. } return (mode); }

w w
w

PD

PD

F-

XC

h a n g e Vi e

F-

XC

h a n g e Vi e

er

er

O W

bu

lic

lic

4
w
.c

to

to

bu

N
w

O W
.d o
c u -tr a c k

.d o

c u -tr a c k

.c

Problem 3: Quick Pointer Questions. Here are the answers for these questions legal means it will compile, and illegal means that the compiler will generate an error:
Number Expression Compiles? Left side type double double double* N/A N/A N/A N/A double Right side type double double double* double* double double double double

1 2 3 4 5 6 7 8

x = *px; *px = x; px = &x; &x = px; &(x+1) = x; &x + 1 = x; *(&(x+1)) = x; *(&(x)+1) = x;

9 10 11 12

x x x x

= = = =

a; a[0]; *(a[1]); (*a)[2];

13 14 15 16 17 18 19 20

x = a[3+1]; x = a[3] +1; x = &((a[3])+1); x = &(a[3]) +1; x = *(&(a[3])+1); px = a; px = a[0]; px = &(a[4]);

Legal Legal Legal Illegal- &x cant be assigned Illegal same reason as #4 Illegal same reason as #4 Illegal cant take &(x + 1) Legal stores into the variable after x on the stack. This will cause run-time problems! Illegal types dont match Legal Illegal a[1] is not a pointer Illegal (*a) is a double, which means we cant use array references on it. Legal Legal Illegal cant take the address of a number or expression Illegal types dont match Legal means the same thing as x = a[4] Legal Illegal types dont match Legal

double double double double

double* double N/A N/A

double double double double double double* double* double*

double double N/A double* double double* double double*

Problem 4: Function Trace The first time Mystery gets called, we see the following memory diagram:
main z y z Mystery y x 6 6 ???

12

w w
w

PD

PD

F-

XC

h a n g e Vi e

F-

XC

h a n g e Vi e

er

er

O W

bu

lic

lic

5
w
.c

to

to

bu

N
w

O W
.d o
c u -tr a c k

.d o

c u -tr a c k

.c

The second time Mystery gets called, it is within the SlapStick function, so the picture looks like this:
main x y z 6 12 12

Slapstick x y z p1 ??? 6

Mystery y x 24

Note that the address of mains z is channeled down through two functions. Also, note that even though the variable p1 does in fact store some value, the value is meaningless because the function has yet to properly initialize it. The program has the following output:
Mystery: x = 12, *y = 6 Main: x = 6, y = 6, z = 12 Mystery: x = 24, *y = 12 SlapStick: *x = 30, y = 24, *z = 12, *p1 = 30 Main: x = 6, y = 30, z = 12

w w
w

PD

PD

F-

XC

h a n g e Vi e

F-

XC

h a n g e Vi e

er

er

O W

bu

lic

lic

6
w
.c

to

to

bu

N
w

O W
.d o
c u -tr a c k

.d o

c u -tr a c k

.c

Problem 5: Memory Diagram

Stack
main 1 toyon 3 kimball branner 6.0 1.5 2.0 i

Heap

???

??? 3 ???

Orphaned!

Problem 6: 2-D Arrays Note that since C++ uses memory in a row-major format, we start by creating an array of double*'s to hold all the rows, and then fill in the numbers by column.
static double** Make2D(double array[], int nRows, int nCols) { double** result; result = new double*[nRows]; for (int i = 0; i < nRows; i++) { result[i] = new double[nCols]; for (int j = 0; j < nCols; j++) { result[i][j] = array[(i * nCols) + j]; } } return result; }

w w
w

PD

PD

F-

XC

h a n g e Vi e

F-

XC

h a n g e Vi e

er

er

O W

bu

to

lic

lic

to

bu

N
.c

O W
w
.d o
c u -tr a c k

.d o

c u -tr a c k

.c

Mehran Sahami CS106X

Handout #17 October 10, 2005

Section Handout #2
Problem 1: Simple file reading Write a function SumFromFile that takes a file name, opens the file and reads numbers out of a file until it reads a blank line. The numbers from the file should be added together and returned as the result of the function. Your prototype should look as follows:
int SumFromFile (string filename);

Remember that you can use functions from the strutils.h header file to help you out (like StringToInteger). Also, you can assume that the file is well formatted and that it contains only one number per line, ending with a totally blank line.

Problem 2: Arrays a la Mode The text discusses finding the mean and the median of an array of integers. The mode of a set of numbers is simply the value that occurs most often. For example, in the array below

43
0

65
1

72
2

75
3

79
4

82
5

82
6

84
7

84
8

84
9

86
10

90
11

94
12

the mean (or average) is the sum of the numbers divided by the number of entries (1020/13 or 78), the median is the value in the midpoint element (array[6] or 82), and the mode is the value occurring most frequently (in this case, 84). Write a function Mode with the following prototype:
int Mode(int array[], int size);

The function takes an integer array and the number of elements in the array, and returns the mode of the array. If there is more than one mode (which happens, for example, if two or more different values each occur more frequently than other values but occur the same number of times as each other), your program may return any of those values as the mode. Your program may not assume that the array is initially sorted, but you may sort it as part of the implementation of Mode by calling the Sort routine. If you get this right away, try to think of a different approach that will do the same thing.

w w
w

w
w

PD

PD

F-

XC

h a n g e Vi e

F-

XC

h a n g e Vi e

er

er

O W

bu

lic

lic

2
w
.c

to

to

bu

N
w

O W
.d o
c u -tr a c k

.d o

c u -tr a c k

.c

Problem 3: Quick Pointer Questions. Take this crash course test on pointers and memory: which of the following would cause compile-time or run-time problems? Also, what types are the left and right hand sides of each expression?
double x, *px, a[5]; x = *px; *px = x; px = &x; &x = px; &(x+1) = x; &x + 1 = x; *(&(x+1)) = x; *(&(x)+1) = x; x = a; x = a[0]; x = *(a[1]); x = (*a)[2]; x = a[3+1]; x = a[3]+1; x = &((a[3])+1); x = &(a[3])+1; x = *(&(a[3])+1); px = a; px = a[0]; px = &(a[4]); /* /* /* /* /* /* /* /* /* /* /* /* /* /* /* /* /* /* /* /* 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 */ */ */ */ */ */ */ */ */ */ */ */ */ */ */ */ */ */ */ */

w w
w

PD

PD

F-

XC

h a n g e Vi e

F-

XC

h a n g e Vi e

er

er

O W

bu

lic

lic

3
w
.c

to

to

bu

N
w

O W
.d o
c u -tr a c k

.d o

c u -tr a c k

.c

Problem 4: Function Trace Trace through the execution of the following program and show the output it produces. Also, draw the state of memory at the point indicated.
/* * File: nonsense.cpp * This program exists only to test your understanding of pointers. */ #include <iostream> #include "genlib.h" int Mystery(int *y, int x); void SlapStick(int *x, int y, int *z); int main() { int x, y, z; x = 5; y = 6; z = Mystery(&x, y); cout << "Main: x = " << x << ", y = " << y << ", z = " << z << endl; SlapStick(&z, y, &y); cout << "Main: x = " << x << ", y = " << y << ", z = " << z << endl; return 0; } int Mystery(int *y, int x) { *y = x; x += *y; cout << "Mystery: x = " << x << ", *y = " << *y << endl;

Draw a diagram indicating the contents of memory at this point


return (x); } void SlapStick(int *x, int y, int *z) { int *p1; y = Mystery(z, *x); p1 = z; z = x; x = p1; *p1 = y + *z/2; cout << "SlapStick: *x = " << *x << ", y = " << y; cout << ", *z = " << *z << ", *p1 = " << *p1 << endl; }

w w
w

PD

PD

F-

XC

h a n g e Vi e

F-

XC

h a n g e Vi e

er

er

O W

bu

lic

lic

4
w
.c

to

to

bu

N
w

O W
.d o
c u -tr a c k

.d o

c u -tr a c k

.c

Problem 5: Memory Diagram Draw a diagram showing the contents of memory at the indicated point. Be sure to differentiate between the stack and the heap.
void HousingDraw(double **flomo, int lag, double wilbur[], int* & roble) { int* stern; int** govCo; stern = new int[2]; govCo = &stern; stern[0] = sizeof(stern) / 3; stern[1] = stern[0] * 3; *flomo = new double[lag]; roble = new int[2]; wilbur++; *(wilbur + 1) = 4; for(lag = 0; lag < *wilbur; lag++) { wilbur[lag] = wilbur[lag] / 2; } roble = *govCo; }

int main() { int* toyon; double *kimball, branner[3]; for(int i = 0; i < 3; i++) { branner[i] = 6 / (i + 1); } HousingDraw(&kimball, branner[2], branner, toyon);

Draw a diagram indicating the contents of memory at this point


return 0; }

w w
w

PD

PD

F-

XC

h a n g e Vi e

F-

XC

h a n g e Vi e

er

er

O W

bu

lic

lic

5
w
.c

to

to

bu

N
w

O W
.d o
c u -tr a c k

.d o

c u -tr a c k

.c

Problem 6: 2-D Arrays Write a function Make2D that takes a one-dimensional array, a row size, and a column size, and returns a 2-D array from the numbers in the first array. Make sure that your function can deal with arrays of different dimensions. You can also assume that there will be enough data in the array to fill the 2-D array of the specified size (i.e. there will be at least row * col elements in the one-dimensional array)

For example, if we provide the following array:


3.7 8.2 4.0 9.5 2.7 6.4 9.8 5.4 9.1 3.5 5.6 7.8 8.7 2.0 9.4 7.1

And the row and column dimensions 4 and 4, we should get a pointer to a dynamically allocated array that looks like this:

Heap
2.7 6.4 9.8

3.7 8.2 4.0 9.5

9.1 5.4 3.5 5.6 8.7 7.8 2.0 9.4 7.1

The prototype is as follows:


double** Make2D(double array[], int rows, int cols);

w w
w

PD

PD

F-

XC

h a n g e Vi e

F-

XC

h a n g e Vi e

er

er

O W

bu

to

lic

lic

to

bu

N
.c

O W
w
.d o
c u -tr a c k

.d o

c u -tr a c k

.c

Mehran Sahami CS106X

Handout #17S October 10, 2005

Section Solutions #2
Problem 1: SumFromFile
#include <iostream> #include <fstream> #include "strutils.h" int SumFromFile(string filename) { int sum = 0, num; ifstream in; in.open(filename.c_str()); // check to make sure the ifstream is valid if (in.fail()) return sum; string line; // read from file until we reach a blank line or end-of-file while (true) { getline(in, line); if (in.fail() || line == "") break; num = StringToInteger(line); sum += num; } in.close(); return sum;

Problem 2: Arrays a la Mode As with many algorithmic problems, there are many ways to accomplish the task of finding a mode. Probably the most straightforward mechanism is to simply count the times each element appears, as exemplified in the code below:
/* * Function: CountElement * ---------------------* Returns the number of times that "value" appears in the array. */ int CountElement(int value, int array[], int size) { int count; count = 0; for (int i = 0; i < size; i++) { if (value == array[i]) { count++; } } return (count);

w w
w

w
w

PD

PD

F-

XC

h a n g e Vi e

F-

XC

h a n g e Vi e

er

er

O W

bu

lic

lic

2
w
.c

to

to

bu

N
w

O W
.d o
c u -tr a c k

.d o

c u -tr a c k

.c

/* * Function: Mode * -------------* This implementation just goes through the array and counts the number of * times each element appears, keeping track of the current maximum value. */ int Mode(int array[], int size) { int mode, max, count; max = 0; for (int i = 0; i < size; i++) { count = CountElement(array[i], array, size); if (count > max) { max = count; mode = array[i]; } } return (mode);

Alternatively, one could sort the array first and then just check the length of each run of consecutive elements. This is slightly more complicated, but certainly more efficient, as shown in this version of Mode (which assumes that we've already defined a standard Sort function):
int Mode(int array[], int size) { int start, count, max, mode; Sort(array, size); // Assume we have this function which sorts an array start = max = 0; count = 1; for (int i = 1; i < size; i++) { if (array[i] == array[start]) { count++; } else { if (count > max) { mode = array[start]; max = count; } start = i; count = 1; } } if (count > max) { mode = array[start]; // Takes care of the last run. } } return (mode);

w w
w

PD

PD

F-

XC

h a n g e Vi e

F-

XC

h a n g e Vi e

er

er

O W

bu

lic

lic

3
w
.c

to

to

bu

N
w

O W
.d o
c u -tr a c k

.d o

c u -tr a c k

.c

Problem 3: Quick Pointer Questions. Here are the answers for these questions legal means it will compile, and illegal means that the compiler will generate an error:
Number 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 Expression x = *px; *px = x; px = &x; &x = px; &(x+1) = x; &x + 1 = x; *(&(x+1)) = x; *(&(x)+1) = x; x x x x = = = = a; a[0]; *(a[1]); (*a)[2]; Compiles? Legal Legal Legal Illegal- &x cant be assigned Illegal same reason as #4 Illegal same reason as #4 Illegal cant take &(x + 1) Legal stores into the variable after x on the stack. This will cause run-time problems! Illegal types dont match Legal Illegal a[1] is not a pointer Illegal (*a) is a double, which means we cant use array references on it. Legal Legal Illegal cant take the address of a number or expression Illegal types dont match Legal means the same thing as x = a[4] Legal Illegal types dont match Legal Left side type double double double* N/A N/A N/A N/A double double double double double double double double double double double* double* double* Right side type double double double* double* double double double double double* double N/A N/A double double N/A double* double double* double double*

x = a[3+1]; x = a[3] +1; x = &((a[3])+1); x = &(a[3]) +1; x = *(&(a[3])+1); px = a; px = a[0]; px = &(a[4]);

Problem 4: Function Trace The first time Mystery gets called, we see the following memory diagram:
main z y z Mystery y x 6 6 ???

12

The second time Mystery gets called, it is within the SlapStick function, so the picture looks like this:

w w
w

PD

PD

F-

XC

h a n g e Vi e

F-

XC

h a n g e Vi e

er

er

O W

bu

lic

lic

4
w
.c

to

to

bu

N
w

O W
.d o
c u -tr a c k

.d o

c u -tr a c k

.c

main x y z Slapstick x y z p1 Mystery y x 24 ??? 6 6 12 12

Note that the address of mains z is channeled down through two functions. Also, note that even though the variable p1 does in fact store some value, the value is meaningless because the function has yet to properly initialize it. The program has the following output:
Mystery: x = 12, *y = 6 Main: x = 6, y = 6, z = 12 Mystery: x = 24, *y = 12 SlapStick: *x = 30, y = 24, *z = 12, *p1 = 30 Main: x = 6, y = 30, z = 12

w w
w

PD

PD

F-

XC

h a n g e Vi e

F-

XC

h a n g e Vi e

er

er

O W

bu

lic

lic

5
w
.c

to

to

bu

N
w

O W
.d o
c u -tr a c k

.d o

c u -tr a c k

.c

Problem 5: Memory Diagram

Stack
main toyon kimball branner 6.0 1.5 2.0 1 3

Heap

??? ???

??? ???

Orphaned!

Problem 6: 2-D Arrays Note that since C++ uses memory in a row-major format, we start by creating an array of double*'s to hold all the rows, and then fill in the numbers by column.
double** Make2D(double array[], int nRows, int nCols) { double** result; result = new double*[nRows]; for (int i = 0; i < nRows; i++) { result[i] = new double[nCols]; for (int j = 0; j < nCols; j++) { result[i][j] = array[(i * nCols) + j]; } } } return result;

w w
w

PD

PD

F-

XC

h a n g e Vi e

F-

XC

h a n g e Vi e

er

er

O W

bu

to

lic

lic

to

bu

N
.c

O W
w
.d o
c u -tr a c k

.d o

c u -tr a c k

.c

CS106 Autumn 06-07

Handout #17 September 9, 2006

Section Handout #2
Problem 1: Using the Scanner and Stack classes
<html><b><i>CS106 rules!</i></b></html>

Web browsers use stacks to track html tags such as <b>, <i> or <html>. Every html tag must be matched by an equivalent closing tag -- <b>, <i> or <html>. Microsoft is looking for programmers to help implement this feature in the next version of Internet Explorer and you, armed with your newly acquired knowledge of classes, decide to volunteer for the job. Using the Scanner class (from A-21 in the reader) and the Stack class (A-27), write the following function:
bool IsCorrectlyNested(string htmlStr);

You can assume that all the tags in the html string will be correctly formed. That is, once you see an angle bracket, it will be followed by the remainder of a complete and wellformed tag (So, nothing like <<html>). Problem 2: Queues Write a function
void ReverseQueue(Queue<int>& q);

that reverses the elements in the passed in queue. (Hint: Is there another class that could make doing this a lot easier?) Problem 3: Vectors (AKA C++ ArrayLists) Say we are writing the next version of Eudora and want to use a Vector (A-31) to store all the data. The following structure is used to hold the data of an email message:
struct eMailMsg { string to; // i.e. "professor@stanford.edu" string from; // i.e. "student@stanford.edu" string message; // body of message string subject; // i.e. "I really like your class, but not as much // as CS106" int date; // date email was sent int time; // time email was sent };

a) How would you declare a Vector that stores eMailMsgs? b) Write a function RemoveSpam that takes a vector containing elements of type eMailMsg and removes all elements whose subject begins with the string "SPAM".

w w
w

w
w

PD

PD

F-

XC

h a n g e Vi e

F-

XC

h a n g e Vi e

er

er

O W

bu

lic

lic

2
w
.c

to

to

bu

N
w

O W
.d o
c u -tr a c k

.d o

c u -tr a c k

.c

c) How could you modify the to field of the eMailMsg structure so that it can hold the email addresses of an arbitrary number of recipients of an email? With the modification in place, given an eMailMsg email, how would you access the last address listed in the to field? Problem 4: Data Structure Design Apple has decided to release new software for the iPod, and since everyone in 106 has just learned about data structures and pointers, decides to ask for your help with the design decisions. For simplicity, each song only needs to store the album, artist, and genre associated with it. a.) How would you design the data structure to represent a song and the list of songs stored on an iPod? b.) Just viewing songs is fun, but Apple also wants the user to be able to have multiple views on the same data. For instance, the user should be able to view by album, by artist or by genre. How would you create the data structures to do this? c.) Now suppose the user connects their iPod to their computer and a song needs to be added to the iPod. What needs to be updated in the data structures to allow this to happen? Also, what happens in the case of deleting? Does your design from parts a and b easily allow you to add and delete? If not, what changes would you make so that these operations would be easier/more efficient? Problem 5: Map Warm-up Write a function:
char MostFrequentCharacter(ifstream &if, int &numOccurrences);

that given an input file stream, returns the character that occurs the most frequently and stores the number of times it occurs in the reference parameter numOccurrences. To write this function, first start by scanning through the file stream, analyzing each character and storing an updated count in a map. Then, after youve built this table, iterate over it to find the character that occurred the most often.

w w
w

PD

PD

F-

XC

h a n g e Vi e

F-

XC

h a n g e Vi e

er

er

O W

bu

to

lic

lic

to

bu

N
.c

O W
w
.d o
c u -tr a c k

.d o

c u -tr a c k

.c

CS106 Autumn 06-07

Handout #17S September 9, 2006

Section Solutions #2
Problem 1: Using the Scanner and Stack classes
#include "stack.h" #include "scanner.h" bool ProcessOpenTag(Scanner& scanner, Stack<string>& tagStack) { string tag = scanner.nextToken(); tagStack.push(tag); return true; } bool ProcessCloseTag(Scanner& scanner, Stack<string>& tagStack) { string tag = scanner.nextToken(); if (!tagStack.isEmpty() && tag == tagStack.pop()) { return true; }else { return false; } } bool ProcessTag(Scanner& scanner, Stack<string>& tagStack) { // read the next token to see if we found an // opening or closing tag string token = scanner.nextToken(); if (token == "/") { return ProcessCloseTag(scanner, tagStack); } else { scanner.saveToken(token); //So ProcessOpenTag can use it return ProcessOpenTag(scanner, tagStack); } } bool IsCorrectlyNested(string htmlStr) { Scanner scanner; scanner.setSpaceOption(Scanner::IgnoreSpaces); Stack<string> tagStack; Scanner.setInput(htmlStr); // start by assuming it is balanced

w w
w

w
w

PD

PD

F-

XC

h a n g e Vi e

F-

XC

h a n g e Vi e

er

er

O W

bu

to

lic

lic

to

bu

N
.c

O W
w
.d o
c u -tr a c k

.d o

c u -tr a c k

.c

bool isBalanced = true; while (scanner.hasMoreTokens()) { string token = scanner.nextToken(); if (token == "<") { if (!ProcessTag(scanner, tagStack)) { isBalanced = false; break; } // get rid of ">" part of tag scanner.nextToken(); } } if (!tagStack.isEmpty()) isBalanced = false; return isBalanced; }

Problem 2: Queues
/** * The client version of reverse queue. In order * to change the order of elements in the queue, * we use an external stack */ void ReverseQueue(Queue<int>& queue) { Stack<int> stack; while (!queue.isEmpty()) stack.push(queue.dequeue()); while (!stack.isEmpty()) queue.enqueue(stack.pop()); }

Problem 3: Vectors a) Vector<eMailMsg> mailVector; b)


void RemoveSpam(Vector<eMailMsg>& v) { for (int i = 0; i < v.size(); i++) { eMailMsg mail = v[i]; if (mail.subject.find("SPAM") == 0) { v.removeAt(i); i--; // look at this index again, // since elements have been shifted down } } }

w w
w

w
w

PD

PD

F-

XC

h a n g e Vi e

F-

XC

h a n g e Vi e

er

er

O W

bu

to

lic

lic

to

bu

N
.c

O W
w
.d o
c u -tr a c k

.d o

c u -tr a c k

.c

Note that you also could move backwards down the vector and not have to worry about decrementing i. c) We use another Vector, of course!
struct eMailMsg { Vector<string> to; string from; string message; string subject; int date; int time; };

Access to the last element of eMailMsg email would be done by:


string lastAddress = email.to[email.to.size() 1];

Problem 4: Data Structure Design a.) To store song information we could create something as follows:
struct songT { string string string string

name; album; artist; genre; //We dont know how long it will be

char *data; };

and then to store the collection of songs, we could simply turn to the Vector class:
Vector<songT *> songs;

b.) To create multiple views, we could define our data structures as below:
struct albumT { string albumName; Vector<songT *> songs; }; struct artistT { string artistName; Vector<songT *> songs; }; typedef enum genreTypeT { Rap, Rock, Jazz, Classical };

w w
w

w
w

PD

PD

F-

XC

h a n g e Vi e

F-

XC

h a n g e Vi e

er

er

O W

bu

to

lic

lic

to

bu

N
.c

O W
w
.d o
c u -tr a c k

.d o

c u -tr a c k

.c

struct genreT { genreTypeT genre; Vector<songT *> songs; };

And then to have multiple views on the data, we simply need to store Vectors of the above information:
Vector<artistT> artists; Vector<albumT> albums; Vector<genreT> genres;

In each of the above, we make heavy use of the Vector to help store all of our data. Note that in all of the structures, we store a pointer to the songT when storing a list of songs. This lets us just have one copy of each song, and then reuse the data associated with that song. If each of the above structures just stored a songT in their vectors rather than a pointer to one, then they each would have a copy of the contents of the songT. This would cause us to store a lot of redundant data Also, if the song name was accidentally mistyped, we only need to change the data in that song for everyone else to notice the change, rather than having to search through everything to find matches to that song. It also might be helpful to store all of the vectors in sorted order. This would allow for convenient browsing, but also would mean wed need to do a little extra work to make sure the array was sorted (for instance, by inserting new songs in to the array in their correct alphabetic position). Also, with these new data structures, we can change up our songT slightly. Rather than storing the name of the album, artist and genre, we can leverage the fact that we now have data structures to store this information, and can point to the associated data structure for each field. Now, if we have a song and want to find the other songs in the same genre, we simply need to follow a pointer to find out all this information.
struct songT { string name; albumT *album; artistT *artist; genreT *genre; char *data; };

c.) To add a song, we need to create a new songT. To do so well need to find the album, artist and genre the song is associated with (or create them if they dont exist). After which well want to add the songT to the list of songs, as well as to the album, artist and genre it belongs to. To delete a song we simply need to look it up, and then remove it from all the views it is associated with. Our use of the Vector means all of these

w w
w

w
w

PD

PD

F-

XC

h a n g e Vi e

F-

XC

h a n g e Vi e

er

er

O W

bu

to

lic

lic

to

bu

N
.c

O W
w
.d o
c u -tr a c k

.d o

c u -tr a c k

.c

operations are fairly easy. If we chose to store raw arrays, wed have a lot more work to do in terms of organizing our data: wed have to make sure to have enough memory allocated, shuffle array elements depending on where we inserted or deleted, make sure to stay in bounds, etc Problem 5: Symbol Table Warm-up
char MostFrequentCharacter(ifstream &in, int &numOccurrences) { Map<int> charFrequencies; numOccurences = 0; while (true) { // get the next character from the stream int nextChar = in.get(); if (nextChar == EOF) { break; } // convert it to a string for lookup in the symbol table string foundChar = ""; foundChar += char(nextChar); // if we find it, incremement the stored value, otherwise // enter in a new one int frequency = 1; if (charFrequencies.containsKey(foundChar) frequency = charFrequencies[foundChar] + 1; charFrequencies[foundChar] = frequency; } // now use an iterator to find the most occurring character Map<int>::Iterator it = charFrequencies.iterator(); string maxCharacter = ""; while (it.hasNext()) { string character = it.next(); int frequency = charFrequencies[character]; if (frequency > numOccurrences) { maxCharacter = character; numOccurrences = frequency; } } return maxCharacter[0]; }

w w
w

w
w

PD

PD

F-

XC

h a n g e Vi e

F-

XC

h a n g e Vi e

er

er

O W

bu

to

lic

lic

to

bu

N
.c

O W
w
.d o
c u -tr a c k

.d o

c u -tr a c k

.c

CS106X Fall 07-08

Handout #16 October 8, 2007

Section Handout #2
Problem 1: Vectors (AKA C++ ArrayLists) Say we are writing the next version of a nifty new mail reading program and want to use a Vector (interface in reader appendix) to store all the data. The following structure is used to hold the data of an email message:
struct eMailMsg { string to; string from; string message; string subject; int date; int time; }; // // // // // // i.e. i.e. body i.e. date time "professor@stanford.edu" "student@stanford.edu" of message "CS106 Rocks!" email was sent email was sent

a) How would you declare a Vector that stores eMailMsgs? b) Write a function RemoveSpam that takes a vector containing elements of type eMailMsg and removes all elements whose subject begins with the string "SPAM". c) How could you modify the to field of the eMailMsg structure so that it can hold the email addresses of an arbitrary number of recipients of an email? With the modification in place, given an eMailMsg email, how would you access the last address listed in the to field? Problem 2: Queues Write a function
void ReverseQueue(Queue<int> & q);

that reverses the elements in the passed in queue. (Hint: Is there another class that could make doing this a lot easier?)

w
w

w
w

PD

PD

F-

XC

h a n g e Vi e

F-

XC

h a n g e Vi e

er

er

O W

bu

to

lic

lic

to

bu

N
.c

O W
w
.d o
c u -tr a c k

.d o

c u -tr a c k

.c

Problem 3: Pointer and Memory Debugging Identify the point and memory bugs in the following code segments. All of the code segments compile but dont perform properly when run. If are having difficulty finding the bugs, one good strategy is to draw a picture of memory and update it as the code progresses. This code does not print the expected values.
const int size = 5; int main() { string * firstArray = new string[size]; string * secondArray = new string[size]; //Code not shownthat populates firstArray and secondArray with //strings firstArray = new string[size]; for(int i = 0; i < size; i++) { firstArray[i] = firstArray[i] + secondArray[i]; cout << firstArray[i] << endl; } delete [] firstArray; delete [] secondArray; return 0; }

(Note: this example was changed slightly from the version given out in class. The substance of the problem is not different. However, while the previous code will run in some compilers, it was not legal standard C++) The array returned from this function seems to have random values in it.
/* InterleaveArrays * Usage:string *arr = InterleaveArrays(firstArray, secondArray, size); * --------------------* Takes as input two arrays whose length is size and returns an array * which alternates values from the two arrays. */ const int size = 5; string *InterleaveArrays(string *firstArray, string *secondArray) { string interleavedArray[2*size]; for(int i = 0; i < size; i++) { interleavedArray[2*i] = firstArray[i]; interleavedArray[2*i+1] = secondArray[i]; } string * allocatedArray = new string[2*size]; allocatedArray = interleavedArray; return allocatedArray; }

w
w

w
w

PD

PD

F-

XC

h a n g e Vi e

F-

XC

h a n g e Vi e

er

er

O W

bu

to

lic

lic

to

bu

N
.c

O W
w
.d o
c u -tr a c k

.d o

c u -tr a c k

.c

Array behaves strangely after it is passed to this function. Sometimes, its values change unexpectedly. (Hint: This example is a bit more difficult than the previous two, and while it is always a good strategy we especially recommend you draw a picture of memory for this problem).
/* DoubleArray * Usage: DoubleArray(array, size); * --------------------* Resizes the given array to be double its current size. */ void DoubleArray(int *array, int size) { int * newArray = new int[2*size]; for(int i = 0; i < size; i++) newArray[i] = array[i]; delete [] array; array = newArray; }

Problem 4: Data Structure Design Apple has decided to release new software for the iPod, and since everyone in 106 has just learned about data structures and pointers, decides to ask for your help with the design decisions. For simplicity, the information you need to store for each each song is the song itself (represented as an array of characters of different length depending on the song), the album name, the artist, and the genre associated with the song. When answering the questions, think about the different tools weve learned for constructing data structures. How do you represent a grouping of different kinds of data as a single object (hint: you might want to do this for each song)? What ways are there to store a collection of objects? When should you use a vector versus an array? When should you store objects versus pointers to those objects? a.) How would you design the data structure to represent a song and the list of songs stored on an iPod? b.) Now were going to add a new piece. Just viewing songs is fun, but Apple also wants the user to be able to have multiple views on the same data. For instance, the user should be able to view by album, by artist or by genre. How would you create the data structures to do this? Be careful not to store redundant data in multiple locations if you can help it. Note that to do this you might need to modify your structure from part a slightly, depending on how you answered. c.) Now suppose the user connects their iPod to their computer and a song needs to be added to the iPod. What needs to be updated in the data structures to allow this to happen? Also, what happens in the case of deleting? Does your design from parts a and b easily allow you to add and delete? If not, what changes would you make so that these operations would be easier/more efficient?

w
w

w
w

PD

PD

F-

XC

h a n g e Vi e

F-

XC

h a n g e Vi e

er

er

O W

bu

to

lic

lic

to

bu

N
.c

O W
w
.d o
c u -tr a c k

.d o

c u -tr a c k

.c

Problem 5: Map Warm-up Write a function:


char MostFrequentCharacter(ifstream &if, int &numOccurrences);

that given an input file stream, returns the character that occurs the most frequently and stores the number of times it occurs in the reference parameter numOccurrences. To write this function, first start by scanning through the file stream, analyzing each character and storing an updated count in a map. Then, after youve built this table, iterate over it to find the character that occurred the most often.

w
w

w
w

PD

PD

F-

XC

h a n g e Vi e

F-

XC

h a n g e Vi e

er

er

O W

bu

to

lic

lic

to

bu

N
.c

O W
w
.d o
c u -tr a c k

.d o

c u -tr a c k

.c

CS106X Fall 07-08

Handout #16S October 8, 2007

Section Solutions #2
Problem 1: Vectors a) Vector<eMailMsg> mailVector; b)
void RemoveSpam(Vector<eMailMsg> & v) { for (int i = 0; i < v.size(); i++) { eMailMsg mail = v[i]; if (mail.subject.find("SPAM") == 0) { v.removeAt(i); i--; // look at this index again, // since elements have been shifted down } } }

Note that you also could move backwards down the vector and not have to worry about decrementing i. c) We use another Vector, of course!
struct eMailMsg { Vector<string> to; string from; string message; string subject; int date; int time; };

Access to the last element of eMailMsg email would be done by:


string lastAddress = email.to[email.to.size() 1];

Problem 2: Queues
/** * The client version of reverse queue. In order * to change the order of elements in the queue, * we use an external stack */ void ReverseQueue(Queue<int> & queue) { Stack<int> stack; while (!queue.isEmpty()) { stack.push(queue.dequeue()); } while (!stack.isEmpty())

w
w

w
w

PD

PD

F-

XC

h a n g e Vi e

F-

XC

h a n g e Vi e

er

er

O W

bu

to

lic

lic

to

bu

N
.c

O W
w
.d o
c u -tr a c k

.d o

c u -tr a c k

.c

{ queue.enqueue(stack.pop()); } }

Problem 3: Debugging The following version of the code has the bugs marked:
const int size = 5; int main() { string * firstArray = new string[size]; string * secondArray = new string[size]; //Code not shownthat populates firstArray and secondArray with //strings //Here we are overwriting the original location of //firstArray. This causes a couple problems. First, we have no //reference to the original location, so when we cannot delete //it. Second, when we try to populate firstArray in the for loop, //we are referring to uninitialized memory instead of the values //of firstArray we set up in the previous code. Instead of using //firstArray again, we should store the returned array in a new //variable so that we have references to all of the arrays we //need to delete. firstArray = new string[size]; for(int i = 0; i < size; i++) { firstArray[i] = firstArray[i] + secondArray[i]; cout << firstArray[i] << endl; } delete [] firstArray; delete [] secondArray; return 0; } /* InterleaveArrays * Usage:string *arr = InterleaveArrays(firstArray, secondArray, size); * --------------------* Takes as input two arrays and their size and returns an array which * alternates values from the two arrays. */ string *InterleaveArrays(string *firstArray, string *secondArray, int size) { string interleavedArray[2*size]; for(int i = 0; i < size; i++) { interleavedArray[2*i] = firstArray[i]; interleavedArray[2*i+1] = secondArray[i]; } string * allocatedArray = new string[2*size];

w
w

w
w

PD

PD

F-

XC

h a n g e Vi e

F-

XC

h a n g e Vi e

er

er

O W

bu

to

lic

lic

to

bu

N
.c

O W
w
.d o
c u -tr a c k

.d o

c u -tr a c k

.c

//This doesnt work as intended. Because allocatedArray and //interleavedArray are both pointers, this will just make //allocatedArray point to interleavedArray. This is bad for //a couple different reasons. First of all, it means that what //we return will point to an invalid stack location. Second, we //overwrite the location of allocatedArray, so we have no way of //deleting it. To properly write this code, we would need to //write a for loop to copy the values of interleavedArray into //allocatedArray. However, it would probably be better to just //declare interleavedArray dynamically to begin with. Note that //this code would work for vectors since they override operator = //so that setting two vectors equal to each other makes a deep //copy. allocatedArray = interleavedArray; return allocatedArray; } /* DoubleArray * Usage: DoubleArray(array, size); * --------------------* Resizes the given array to be double its current size. */ //This function doesnt work as intended because array should be passed //by reference, not by value. This would allow us to change what it //points to as we desire. What this code actually does is allocate a //new array and then set the local array pointer to point to it. This //new array is orphaned in memory when the function returns, and the //array in the calling function still points to the same place as //before. However, that memory was deallocated in DoubleArray, so it is //no longer valid to point there. Therefore, over time, the array will //be changed because the memory is no longer reserved for that array. void DoubleArray(int *array, int size) { int * newArray = new int[2*size]; for(int i = 0; i < size; i++) newArray[i] = array[i]; delete [] array; array = newArray; }

Problem 4: Data Structure Design a.) To store song information we could create something as follows:
struct songT { string string string string };

name; album; artist; genre; //We dont know how long it will be

char *data;

w
w

w
w

PD

PD

F-

XC

h a n g e Vi e

F-

XC

h a n g e Vi e

er

er

O W

bu

to

lic

lic

to

bu

N
.c

O W
w
.d o
c u -tr a c k

.d o

c u -tr a c k

.c

and then to store the collection of songs, we could simply turn to the Vector class:
Vector<songT> songs;

b.) In the previous step we represented a list of songs as a vector of songTs. This makes sense given that all we were doing was storing them. However, now we want to have multiple views on the data. To do this, it makes sense to instead store vectors of pointers to songTs. To create multiple views, we could therefore define our data structures as below:
struct albumT { string albumName; Vector<songT *> songs; }; struct artistT { string artistName; Vector<songT *> songs; }; typedef enum genreTypeT { Rap, Rock, Jazz, Classical }; struct genreT { genreTypeT genre; Vector<songT *> songs; };

And then to have multiple views on the data, we simply need to store Vectors of the above information:
Vector<artistT> artists; Vector<albumT> albums; Vector<genreT> genres;

In each of the above, we make heavy use of the Vector to help store all of our data. Also, by changing our representation of a list of songs to use pointers, we can just have one copy of each song, and then reuse the data associated with that song. If each of the above structures just stored a songT in their vectors as in the previous part rather than a pointer to one, then they each would have a copy of the contents of the songT. This would cause us to store a lot of redundant data Also, if the song name was accidentally mistyped, we only need to change the data in that song for everyone else to notice the change, rather than having to search through everything to find matches to that song. It also might be helpful to store all of the vectors in sorted order. This would allow for convenient browsing, but also would mean wed need to do a little extra work to make sure the array was sorted (for instance, by inserting new songs in to the array in their correct alphabetic position).

w
w

w
w

PD

PD

F-

XC

h a n g e Vi e

F-

XC

h a n g e Vi e

er

er

O W

bu

to

lic

lic

to

bu

N
.c

O W
w
.d o
c u -tr a c k

.d o

c u -tr a c k

.c

Also, with these new data structures, we can change up our songT slightly. Rather than storing the name of the album, artist and genre, we can leverage the fact that we now have data structures to store this information, and can point to the associated data structure for each field. Now, if we have a song and want to find the other songs in the same genre, we simply need to follow a pointer to find out all this information.
struct songT { string name; albumT *album; artistT *artist; genreT *genre; char *data; };

c.) To add a song, we need to create a new songT. To do so well need to find the album, artist and genre the song is associated with (or create them if they dont exist). After which well want to add the songT to the list of songs, as well as to the album, artist and genre it belongs to. To delete a song we simply need to look it up, and then remove it from all the views it is associated with. Our use of the Vector means all of these operations are fairly easy. If we chose to store raw arrays, wed have a lot more work to do in terms of organizing our data: wed have to make sure to have enough memory allocated, shuffle array elements depending on where we inserted or deleted, make sure to stay in bounds, etc

w
w

w
w

PD

PD

F-

XC

h a n g e Vi e

F-

XC

h a n g e Vi e

er

er

O W

bu

to

lic

lic

to

bu

N
.c

O W
w
.d o
c u -tr a c k

.d o

c u -tr a c k

.c

Problem 5: Map Warm-up


char MostFrequentCharacter(ifstream &in, int &numOccurrences) { Map<int> charFrequencies; numOccurrences = 0; char nextChar; while((nextChar = in.get()) != EOF) { // convert it to a string for lookup in the symbol table string foundChar = ""; foundChar += char(nextChar); // if we find it, incremement the stored value, otherwise // enter in a new one int frequency = 1; if (charFrequencies.containsKey(foundChar) frequency = charFrequencies[foundChar] + 1; charFrequencies[foundChar] = frequency; } // now use an iterator to find the most occurring character Map<int>::Iterator it = charFrequencies.iterator(); string maxCharacter = ""; while (it.hasNext()) { string character = it.next(); int frequency = charFrequencies[character]; if (frequency > numOccurrences) { maxCharacter = character; numOccurrences = frequency; } } return maxCharacter[0]; }

w
w

w
w

PD

PD

F-

XC

h a n g e Vi e

F-

XC

h a n g e Vi e

er

er

O W

bu

to

lic

lic

to

bu

N
.c

O W
w
.d o
c u -tr a c k

.d o

c u -tr a c k

.c

Tom Hurlbutt CS106X

Handout #14 October 6, 2008

Section Handout #2
Problem 1: 2-D Arrays Now that youre familiar with using the Grid class, its time to think about how they are implemented. At its heart, every Grid is a dynamically-allocated 2-D array. Now, write your own function Make2D that takes a one-dimensional array, a row size, and a column size, and returns a 2-D array from the numbers in the first array. Make sure that your function can deal with arrays of different dimensions. You can also assume that there will be enough data in the array to fill the 2-D array of the specified size (i.e. there will be at least row * col elements in the one-dimensional array) For example, if we provide the following array:
3.7 8.2 4.0 9.5 2.7 6.4 9.8 5.4 9.1 3.5 5.6 7.8 8.7 2.0 9.4 7.1

And the row and column dimensions 4 and 4, we should get a pointer to a dynamically allocated array that looks like this:

The prototype is as follows:


double** Make2D(double array[], int rows, int cols);

w
w

w
w

PD

PD

F-

XC

h a n g e Vi e

F-

XC

h a n g e Vi e

er

er

O W

bu

lic

lic

2
w
.c

to

to

bu

N
w

O W
.d o
c u -tr a c k

.d o

c u -tr a c k

.c

Problem 2: Using the Scanner and Stack classes


<html><b><i>CS106X rules!</i></b></html>

Web browsers use stacks to track HTML tags such as <b>, <i> or <html>. Every HTML tag must be matched by an equivalent closing tag<b>, <i> or <html>. Microsoft is looking for programmers to help implement this feature in the next version of Internet Explorer and you, armed with your newly acquired knowledge of classes, decide to volunteer for the job. Using the Scanner and Stack classes to write the following function:
bool IsCorrectlyNested(string htmlStr);

You can assume that all the tags in the html string will be correctly formed. That is, once you see an angle bracket, it will be followed by the remainder of a complete and wellformed tag (So, nothing like "<<html>"). Problem 3: Queues Write a function
void ReverseQueue(Queue<int>& q);

that reverses the elements in the passed in queue. (Hint: Is there another class that could make doing this a lot easier?) Problem 4: The Writing Center In an effort to improve your English Literature grade, youve decided to write a program that reads in one file (presumably your first draft) and writes the improved version to another. Feedback on prior papers suggests youd do better to cut out the adjectives and to use stronger verbs. So, youd like to automate the rewrite process. (Or rather, "youd proposition to automatize the rewrite process." Wow! That is SO MUCH CLEARER!) Write a function called ImprovePaper, which reads your first draft from the specified ifstream& and writes the "better" version to the specified ofstream&, using the supplied thesaurus (which maps words to synonym sets), and removes all adjectives and replaces all verbs with the longest synonym in the corresponding synonym set. Fortunately, someone else gave you code that magically tells you whether a word is being used as a verb or an adjective. These functions have the following prototypes:
bool IsAdjective(string word); bool IsVerb(string word);

w
w

PD

PD

F-

XC

h a n g e Vi e

F-

XC

h a n g e Vi e

er

er

O W

bu

lic

lic

3
w
.c

to

to

bu

N
w

O W
.d o
c u -tr a c k

.d o

c u -tr a c k

.c

You should assume that the files have been properly opened and that no error checking (beyond the end-of-file check) need be done. You should assume that the supplied thesaurus is fully populated, but you should not assume that it contains all the verbs in your paper. You should preserve spacing, and you shouldnt worry about any remnant punctuation that remains because a series of adjectives were removed. And as far as the verbs are concerned, you should assume the longer the verb, the stronger it is. If the original word happens to be longer/stronger than all of its synonyms, then retain the original word.
void ImprovePaper(ifstream& in, ofstream& out, Map<Set<string> >& thes) { // write to out as you would to cout out << "Heres the better paper" << endl; out << "-----------------------" << endl; // continue with your own code...

Problem 5: Data Structure Design Apple, has decided to release new software for the iPod, and since everyone in 106 has just learned about data structures and pointers, decides to ask for your help with the design decisions. (Fortunately for you, they havent heard about your recent moonlighting for Microsoft!) For simplicity, each song only needs to store the album, artist, and genre associated with it. a.) How would you design the data structure to represent a song and the list of songs stored on an iPod? b.) Just viewing songs is fun, but Apple also wants the user to be able to have multiple views on the same data. For instance, the user should be able to view by album, by artist or by genre. How would you create the data structures to do this? c.) Now, suppose the user connects their iPod to their computer and a song needs to be added to the iPod. What needs to be updated in the data structures to allow this to happen? Also, what happens in the case of deleting? Does your design from parts a and b easily allow you to add and delete? If not, what changes would you make so that these operations would be easier/more efficient?

w
w

PD

PD

F-

XC

h a n g e Vi e

F-

XC

h a n g e Vi e

er

er

O W

bu

lic

lic

4
w
.c

to

to

bu

N
w

O W
.d o
c u -tr a c k

.d o

c u -tr a c k

.c

Problem 6: Map Practice Write a function:


char MostFrequentCharacter(ifstream &if, int &numOccurrences);

that given an input file stream, returns the character that occurs the most frequently and stores the number of times it occurs in the reference parameter numOccurrences. To write this function, first start by scanning through the file stream, analyzing each character and storing an updated count in a map. Then, after youve built this table, iterate over it to find the character that occurred the most often.

w
w

PD

PD

F-

XC

h a n g e Vi e

F-

XC

h a n g e Vi e

er

er

O W

bu

lic

lic

1
w
.c

to

to

bu

N
w

O W
.d o
c u -tr a c k

.d o

c u -tr a c k

.c

Tom Hurlbutt CS106X

Handout #14S October 6, 2008

Section Solutions #2
Problem 1: 2-D Arrays Note that since C++ uses memory in a row-major format, we start by creating an array of double*'s to hold all the rows, and then fill in the numbers by column. (Row-major format here means that arrays are represented as 1-D arrays of rows, rather than 1-D arrays of columns.)
double** Make2D(double array[], int nRows, int nCols) { double** result; result = new double*[nRows]; for (int i = 0; i < nRows; i++) { result[i] = new double[nCols]; for (int j = 0; j < nCols; j++) { result[i][j] = array[(i * nCols) + j]; } } return result; }

Problem 2: Using the Scanner and Stack classes


#include "stack.h" #include "scanner.h" bool ProcessOpenTag(Scanner& scanner, Stack<string>& tagStack) { string tag = scanner.nextToken(); tagStack.push(tag); return true; } bool ProcessCloseTag(Scanner& scanner, Stack<string>& tagStack) { string tag = scanner.nextToken(); if (!tagStack.isEmpty() && tag == tagStack.pop()) { return true; }else { return false; }

w
w

PD

PD

F-

XC

h a n g e Vi e

F-

XC

h a n g e Vi e

er

er

O W

bu

lic

lic

2
w
.c

to

to

bu

N
w

O W
.d o
c u -tr a c k

.d o

c u -tr a c k

.c

bool ProcessTag(Scanner& scanner, Stack<string>& tagStack) { // read the next token to see if we found an // opening or closing tag string token = scanner.nextToken(); if (token == "/") { return ProcessCloseTag(scanner, tagStack); } else { scanner.saveToken(token); //So ProcessOpenTag can use it return ProcessOpenTag(scanner, tagStack); }

bool IsCorrectlyNested(string htmlStr) { Scanner scanner; scanner.setSpaceOption(Scanner::IgnoreSpaces); Stack<string> tagStack; Scanner.setInput(htmlStr); // start by assuming it is balanced bool isBalanced = true; while (scanner.hasMoreTokens()) { string token = scanner.nextToken(); if (token == "<") { if (!ProcessTag(scanner, tagStack)) { isBalanced = false; break; } // get rid of ">" part of tag scanner.nextToken(); } }

if (!tagStack.isEmpty()) isBalanced = false; } return isBalanced;

w
w

PD

PD

F-

XC

h a n g e Vi e

F-

XC

h a n g e Vi e

er

er

O W

bu

lic

lic

3
w
.c

to

to

bu

N
w

O W
.d o
c u -tr a c k

.d o

c u -tr a c k

.c

Problem 3: Queues
/** * The client version of reverse queue. In order * to change the order of elements in the queue, * we use an external stack */ void ReverseQueue(Queue<int>& queue) { Stack<int> stack; while (!queue.isEmpty()) stack.push(queue.dequeue()); while (!stack.isEmpty()) queue.enqueue(stack.pop()); }

Problem 4: The Writing Center


void ImprovePaper(ifstream& in, ofstream& out, Map<Set<string> >& thes) { while (true) { string line; getline(in, line); if (in.eof()) break; ProcessLine(line, out, thes); } } void ProcessLine(string line, ofstream& out, Map<Set<string> >& thes) { Scanner scanner; scanner.setInputLine(line); while (scanner.hasMoreTokens()) { string token = scanner.nextToken(); if (!IsAdjective(token)) { if (IsVerb(token)) { token = FindMostImpressiveWord(token, thes); } out << token; } } out << endl; } string FindMostImpressiveWord(string verb, Map<Set<string> >& thes) { if (!thes.containsKey(verb)) return verb; Set<string>& synonyms = thes[verb]; string strongestVerb = verb; Set<string>::Iterator iter = synonyms.iterator(); while (iter.hasNext()) { string synonym = iter.next();

w
w

PD

PD

F-

XC

h a n g e Vi e

F-

XC

h a n g e Vi e

er

er

O W

bu

lic

lic

4
w
.c

to

to

bu

N
w

O W
.d o
c u -tr a c k

.d o

c u -tr a c k

.c

if (synonym.length() > strongestVerb.length()) { strongestVerb = synonym; } } return strongestVerb; }

Problem 5: Data Structure Design a.) To store song information we could create something as follows:
struct songT { string string string string };

name; album; artist; genre; //We dont know how long it will be

char *data;

and then to store the collection of songs, we could simply turn to the Vector class:
Vector<songT *> songs;

b.) To create multiple views, we could define our data structures as below:
struct albumT { string albumName; }; Vector<songT *> songs;

struct artistT { string artistName; }; Vector<songT *> songs;

typedef enum genreTypeT { Rap, Rock, Jazz, Classical }; struct genreT { genreTypeT genre; }; Vector<songT *> songs;

And then to have multiple views on the data, we simply need to store Vectors of the above information:

w
w

PD

PD

F-

XC

h a n g e Vi e

F-

XC

h a n g e Vi e

er

er

O W

bu

lic

lic

5
w
.c

to

to

bu

N
w

O W
.d o
c u -tr a c k

.d o

c u -tr a c k

.c

Vector<artistT> artists; Vector<albumT> albums; Vector<genreT> genres;

In each of the above, we make heavy use of the Vector to help store all of our data. Note that in all of the structures, we store a pointer to the songT when storing a list of songs. This lets us just have one copy of each song, and then reuse the data associated with that song. If each of the above structures just stored a songT in their vectors rather than a pointer to one, then they each would have a copy of the contents of the songT. This would cause us to store a lot of redundant data Also, if the song name was accidentally mistyped, we only need to change the data in that song for everyone else to notice the change, rather than having to search through everything to find matches to that song. It also might be helpful to store all of the vectors in sorted order. This would allow for convenient browsing, but also would mean wed need to do a little extra work to make sure the array was sorted (for instance, by inserting new songs in to the array in their correct alphabetic position). Also, with these new data structures, we can change up our songT slightly. Rather than storing the name of the album, artist and genre, we can leverage the fact that we now have data structures to store this information, and can point to the associated data structure for each field. Now, if we have a song and want to find the other songs in the same genre, we simply need to follow a pointer to find out all this information.
struct songT { string name; albumT *album; artistT *artist; genreT *genre; }; char *data;

c.) To add a song, we need to create a new songT. To do so well need to find the album, artist and genre the song is associated with (or create them if they dont exist). After which well want to add the songT to the list of songs, as well as to the album, artist and genre it belongs to. To delete a song we simply need to look it up, and then remove it from all the views it is associated with. Our use of the Vector means all of these operations are fairly easy. If we chose to store raw arrays, wed have a lot more work to do in terms of organizing our data: wed have to make sure to have enough memory allocated, shuffle array elements depending on where we inserted or deleted, make sure to stay in bounds, etc

w
w

PD

PD

F-

XC

h a n g e Vi e

F-

XC

h a n g e Vi e

er

er

O W

bu

lic

lic

6
w
.c

to

to

bu

N
w

O W
.d o
c u -tr a c k

.d o

c u -tr a c k

.c

Problem 6: Map Practice


char MostFrequentCharacter(ifstream &in, int &numOccurrences) { Map<int> charFrequencies; numOccurences = 0; while (true) { // get the next character from the stream int nextChar = in.get(); if (nextChar == EOF) { break; } // convert it to a string for lookup in the symbol table string foundChar = ""; foundChar += char(nextChar); // if we find it, incremement the stored value, otherwise // enter in a new one int frequency = 1; if (charFrequencies.containsKey(foundChar) frequency = charFrequencies[foundChar] + 1; charFrequencies[foundChar] = frequency;

// now use an iterator to find the most occurring character Map<int>::Iterator it = charFrequencies.iterator(); string maxCharacter = ""; while (it.hasNext()) { string character = it.next(); int frequency = charFrequencies[character]; if (frequency > numOccurrences) { maxCharacter = character; numOccurrences = frequency; } } } return maxCharacter[0];

w
w

PD

PD

F-

XC

h a n g e Vi e

F-

XC

h a n g e Vi e

er

er

O W

bu

to

lic

lic

to

bu

N
.c

O W
w
.d o
c u -tr a c k

.d o

c u -tr a c k

.c

CS106X Autumn 2009

Handout 12

Section Handout

October 5th, 2009

Problem 1: String Explosions Given a string str and a character delim, write a function called Explode which returns the "explosion" of str in a Vector<string>, which is the ordered collection of strs delim-delimited substrings. Here are some examples of how Explode should work: Explode("171.64.42.111", '.'); should return ["171", "64", "42", "111"] Explode("usr/class/cs106x/WWW", '/'); should return ["usr", "class", cs106x", "WWW"] Explode("XOXXOOOXXOOX", 'X) should return ["", "O", "", "OOO", "", "OO", ""] Note the last example makes it clear what happens when the delim characters appear on either end of the str, or when two instances of delim appear consecutively. In general, a string str with k instances of delim returns a Vector<string> of length k + 1.
Vector<string> Explode(string str, char delim);

Problem 2: URL Parameter Map A URL has many components. a protocol, a domain, and a file path are almost always included. Sometimes the URL includes a query, which the portion of the URL that appears after the ? and (if present) before the # (which defines the URLs hash). Here are some examples: http://www.google.com/ig?hl=en&source=iglk http://www.facebook.com/profile.php?id=1160&viewas=214707 http://store.apple.com/us/browse/family/iphone?mco=MTAyNTM5ODU

And while you may recognize the ? as a common character within URLs, you may not realize that the part following the ? is the serialization of a Map<string>. The query string is an &delimited string of key-value pairs, where each key-value pair takes the form <key>=<value>. When a web server gets an HTTP request, it digests the query string and re-hydrates it into Map<string> form, and uses that map to programmatically shape how the response page is generated. Thats why www.google.com/ig?hl=en&source=iglk generates English, and www.google.com/ig?hl=fr&source=iglk generates French. One little wrinkle: the value is technically optional, so that query strings like type=5&seeall and source_id=9074&read= are legitimate. When a key is present without a value, then

w
w

w
w

PD

PD

F-

XC

h a n g e Vi e

F-

XC

h a n g e Vi e

er

er

O W

bu

2
w
w
o
.c

to

lic

lic

to

bu

N
w

O W
.d o
c u -tr a c k

.d o

c u -tr a c k

.c

the value is arbitrary and the presence of the key is the only thing of interest. In such cases, the Map<string> should include the key and attach an arbitrary value to it. Write a function that takes a legitimate URL, extracts the query string, and returns a Map<string> with all of its key-value pairs. You can safely assume that the URL is properly formatted, and that the structure of an URL is no more complicated than whats described here. Make sure you deal with URLs that dont include a query string, and URLs that include a hash.
Map<string> extractQueryMap(string url);

This problem is as much about string manipulation as it is with map creation, so dont be surprised if youre using some advanced string methods that havent come up in any previous examples. Problem 3: Happy Numbers Starting with any positive integer n, replace n with the sum of the squares of its digits, and repeat the process until the number equals 1, or until you arrive at a previously arrived at number (and have thus entered a cycle). Numbers eventually leading to 1 are called happy numbers, and the rest are called unhappy, or sad, numbers. Using a Set<int>, write the IsHappy predicate function, which accepts a positive number and returns true if and only if the number is happy, and false otherwise.
bool IsHappy(int n);

Problem 4: FilterPossibilities Implement the FilterPossibilities function, which takes a reference to a Set<string> called words and a reference to a Map<int> called letters, and removes strings from words unless they meet the requirements imposed by the letters map. The letters map maps characters (stored as one-character strings) to the maximum number of times that letter may appear in a word if its to be retained by the words set. (If the character doesnt appear as a key in the map, then there are no restrictions on that character.) For instance, if the words set contains "abacus", "gizmo", "holler", "llama", and "portions", and the letters map maps "a" to 1, "l" to 1, and "p" to 3, then FilterPossibilities would remove "abacus" (too many as), "holler" (too many ls), and "llama" (too many of both!), leaving just "gizmo" and "portions".
void FilterPossibilities(Set<string>& words, Map<int>& letters);

w
w

PD

PD

F-

XC

h a n g e Vi e

F-

XC

h a n g e Vi e

er

er

O W

bu

to

lic

lic

to

bu

N
.c

O W
w
.d o
c u -tr a c k

.d o

c u -tr a c k

.c

CS106X Autumn 2009

Handout 12S

Section Solution

October 7th and 8th, 2009

Solution 1: String Explosions


Vector<string> Explode(string str, char delim) { Vector<string> explosion; string cluster; str += delim; // last cluster gets appended w/ minimal special casing for (int i = 0; i < str.size(); i++) { if (str[i] == delim) { explosion.add(cluster); cluster.clear(); // cluster = "" works, too } else { cluster += str[i]; } } return explosion; }

Solution 2: URL Parameter Map


Map<string> extractQueryMap(string url) { Map<string> parameters; int index = url.find('?'); if (index == string::npos) return parameters; string query = url.substr(index + 1); index = query.find('#'); if (index != string::npos) { query = query.substr(0, index); // chop off hash } Vector<string> pairs = Explode(query, '&'); for (int i = 0; i < pairs.size(); i++) { Vector<string> components = Explode(pairs[i], '='); string key = components[0]; string value = "true"; if (components.size() > 1 && !components[1].empty()) { value = components[1]; } parameters[key] = value; } return parameters; }

w
w

w
w

PD

PD

F-

XC

h a n g e Vi e

F-

XC

h a n g e Vi e

er

er

O W

bu

2
w
w
o
.c

to

lic

lic

to

bu

N
w

O W
.d o
c u -tr a c k

.d o

c u -tr a c k

.c

Solution 3: Happy Numbers


int ComputeNext(int num) { int sumOfSquares = 0; while (num > 0) { int digit = num % 10; sumOfSquares += digit * digit; num /= 10; } return sumOfSquares; } bool IsHappy(int num) { Set<int> previouslySeen; while (num > 1 && !previouslySeen.contains(num)) { previouslySeen.add(num); num = ComputeNext(num); } return num == 1; }

Solution 4: FilterPossibilities
int CountLetters(string str, char letter) { int count = 0; for (int i = 0; i < str.size(); i++) { if (str[i] == letter) { count++; } } return count; } void FilterPossibilities(Set<string>& words, Map<int>& lettersMap) { Set<string> wordsToDelete; foreach (string word in words) { foreach (string key in letterMap) { char letter = key[0]; if (CountLetters(word, letter) > lettersMap[key]) { wordsToDelete.add(word); break; // words already been marked for removal... move on } } Another solution might build up a letter frequency } words.subtract(wordsToDelete); }

map of all of the words in words, and that make sure that all counts in this frequency map are less than the corresponding counts in lettersMap.

w
w

PD

PD

F-

XC

h a n g e Vi e

F-

XC

h a n g e Vi e

er

er

O W

bu

to

lic

lic

to

bu

N
.c

O W
w
.d o
c u -tr a c k

.d o

c u -tr a c k

.c

CS106X Autumn 2010

Handout 15 October 4th, 2010

Section Handout
Problem 1: URL Parameter Map A URL has many components. a protocol, a domain, and a file path are almost always included. Sometimes the URL includes a query, which the portion of the URL that appears after the ? and (if present) before the # (which defines the URLs hash). Here are some examples: http://www.google.com/ig?hl=en&source=iglk http://www.facebook.com/profile.php?id=1160&viewas=214707 http://store.apple.com/us/browse/family/iphone?mco=MTAyNTM5ODU

And while you may recognize the ? as a common character within URLs, you may not realize that the part following the ? is the serialization of a Map<string>. The query string is an &delimited string of key-value pairs, where each key-value pair takes the form <key>=<value>. When a web server gets an HTTP request, it digests the query string and re-hydrates it into Map<string> form, and uses that map to programmatically shape how the response page is generated. Thats why www.google.com/ig?hl=en&source=iglk generates English, and www.google.com/ig?hl=fr&source=iglk generates French. One little wrinkle: the value is technically optional, so that query strings like type=5&seeall and source_id=9074&read= are legitimate. When a key is present without a value, then the value is arbitrary and the presence of the key is the only thing of interest. In such cases, the Map<string> should include the key and attach an arbitrary value to it. Write a function that takes a legitimate URL, extracts the query string, and returns a Map<string> with all of its key-value pairs. You can safely assume that the URL is properly formatted, and that the structure of an URL is no more complicated than whats described here. Make sure you deal with URLs that dont include a query string, and URLs that include a hash.
Map<string> extractQueryMap(string url);

This problem is as much about string manipulation as it is about the Map, so dont be surprised if youre using some advanced string methods that havent come up in any previous examples. Problem 2: Publishing Stories Social networking sites like Facebook, LinkedIn, Orkut, Friendster, and MySpace typically record and publish stories about actions taken by you and your friends. Stories such as:

w
w

w
w

PD

PD

F-

XC

h a n g e Vi e

F-

XC

h a n g e Vi e

er

er

O W

bu

2
w
w
o
.c

to

lic

lic

to

bu

N
w
.d o
c u -tr a c k

O W

.d o

c u -tr a c k

.c

John Dixon accepted your friend request. Jeff Barbose is no longer in a relationship. Scott James wrote a note called "The Two Percent Solution". Arlene Heitner commented on Melodie Bowshers video. Antonio Melara gave The French Laundry a 5-star review. are created from story templates like {name} accepted your friend request. {name} is no longer in a relationship. {name} wrote a note called "{title}". {name} commented on {target}s video. {actor} gave {restaurant} a {rating}-star review. The specific story is generated from the skeletal one by replacing the tokenssubstrings like "{name}", "{title}", and "{rating}"with event-specific values, like "John Dixon", "The Two Percent Solution", and "5". The token-value pairs can be packaged in a Map<string>, and given a story template and a data map, its possible to generate an actual story. Write the SubstituteTokens function, which accepts a story template (like "{actor} gave {restaurant} a {rating}-star review.") and a Map<string> (which might map "actor" to "Antonio Melara", "restaurant" to "The French Laundry", and "rating" to "5"), and builds a string just like the story template, except that the tokens have been replaced by the text they map to. Assume the following is true: '{' and '}' exist to delimit token names, but wont appear anywhere else. In other words, if you encounter the '{' character, you can assume it marks the beginning of a token that ends with a '}'. We guarantee that all tokens are in the Map<string>. You dont need to do any error checking. Note that the '{' and the '}' arent included in the map. "{name}", for instance, is replaced with whatever "name" identifies in the map. The prototype is:
string SubstituteTokens(string storyTemplate, Map<string>& data);

w
w

PD

PD

F-

XC

h a n g e Vi e

F-

XC

h a n g e Vi e

er

er

O W

bu

3
w
w
o
.c

to

lic

lic

to

bu

N
w

O W
.d o
c u -tr a c k

.d o

c u -tr a c k

.c

Problem 3: Chain Reactions Chain Reaction is a popular one-player game making its way through the Internet and various mobile platforms. In our version, weve given a collection of locations(x,y) coordinates in the planeof land mines. The detonation of any single land mine prompts all land mines within a certain distance to simultaneously detonate a second later, which themselves set off more land mines another second later, and so forth, and so forth. The chain reaction continues until there are no active land mines, or until none of the remaining land mines happen to fall within the threshold distance of those that have exploded. You get 0 points for the land mine you initially [and manually] detonate. You get 100 points for each land mine that detonates at the one-second mark. You get 400 points for each land mine that detonates at the two-second mark. You get 900 points for each land mine that detonates at the three-second mark. In general, you get 100n2 points for each land mine that detonates at the n-second mark.

Write the function called OptimalInitLocation, which returns the location of the land mine that should be detonated first if the goal is to maximize the overall score. If there are multiple initial detonations that lead to the same maximum score, then you can return any one of them. Refer to the following type definitions and helper functions:
struct location { double x; double y; }; int LocationCompare(location one, location two) { if (one.x == two.x) return OperatorCmp(one.y, two.y); return OperatorCmp(one.x, two.x); } static const double kThresholdDist = 1.5; bool IsInRange(location one, location two) { double deltax = one.x - two.x; double deltay = one.y - two.y; return sqrt(deltax * deltax + deltay * deltay) < kThresholdDist; } location OptimalInitLocation(Set<location>& landmines);

w
w

PD

PD

F-

XC

h a n g e Vi e

F-

XC

h a n g e Vi e

er

er

O W

bu

4
w
w
o
.c

to

lic

lic

to

bu

N
w

O W
.d o
c u -tr a c k

.d o

c u -tr a c k

.c

Problem 4: Making Change Everyone who's worked a cash register (that is to say, me, at least) has had to deal with the intellectually stimulating task of making change. The customer is owed $.90, so do I give her 9 dimes? 3 quarters, a dime, and a nickel? 90 pennies? Were used to 25, 10, 5, and 1 cent denominations, but how might I give change if coins are worth 20, 13, 4, and 1 cents instead? There are clearly many different configurations of coins that will work, but let's say that were interested in the combination that uses the fewest coins. One approach would be to use as many high value coins (i.e. quarters) as possible, then move on to use dimes for what is leftover, then nickels and finally pennies if needed. This type of algorithm is known as a greedy algorithm, since at any given moment it makes the choice that looks best at the moment, the hope being that the locally optimal choice will lead to a globally optimal solution. However, what if I had no nickels in my change drawer and was trying to make $.31? The greedy solution chooses 1 quarter and 6 pennies, which is worse than the optimal solution of 3 dimes and 1 penny. Clearly we need something even smarter to find the truly optimal arrangement. Recursion to the rescue! Write a function MakeChange that takes an amount along with the Vector of available coin values. You can assume you have as many of each coin as you want (i.e. if your cash drawer has pennies, it has an infinite supply of them). The function should return the minimum number of coins required that sum to the given amount. If the amount cannot be made (for example, if you try to make $.31 and have no pennies), the function should return -1. You do not have to print or return the coin combination, just return the minimum number of coins in the optimal configuration. There are several different ways to recursively decompose this problem. Let your creativity lead you toward the one that makes the most sense to you.
int MakeChange(int amount, Vector<int>& denominations);

(Can you write a function that computes the total number of ways to make change using the specified denominations? Thats a related but equally interesting problem.) Problem 5: Towers Of Hanoi Revisited One of the first procedural recursion problems we discussed in lecture was the classic Towers Of Hanoi problem. The recursive solution lists a series of moves needed to move a tower of disks from one location to a second. The solution I provided in class was more or less this:

w
w

PD

PD

F-

XC

h a n g e Vi e

F-

XC

h a n g e Vi e

er

er

O W

bu

5
w
w
o
.c

to

lic

lic

to

bu

N
w

O W
.d o
c u -tr a c k

.d o

c u -tr a c k

.c

void MoveTower(int numDisks, string start, string dest, string temp) { if (numDisks > 0) { MoveTower(numDisks - 1, start, temp, dest); MoveDisk(start, dest); MoveTower(numDisks - 1, temp, dest, start); } } void MoveDisk(string start, string dest) { cout << "Move top disk from " << start << " to " << dest << "." << endl; }

Consider the similar problem of moving another type of tower from the leftmost needle to the rightmost needle.

Note that each size is represented twice: once as white and once as dark grey, and that the number of disks is always even. Note the alternating color scheme of the original tower must be re-established once the recursion is complete. So, the MoveTower function that solves this particular problem must ensure that: only one disk is moved at a time no disk is ever placed on top of a smaller disk the alternating color scheme of the initial tower is re-established exactly in the final towerthat means grey on the bottom, and then white, grey, white, grey, etc.

On the next page, I provide you with four (possibly incorrect) versions of MoveTower. Indicate whether or not the particular implementation is valid, and briefly explain what the incorrect implementations will do.

w
w

PD

PD

F-

XC

h a n g e Vi e

F-

XC

h a n g e Vi e

er

er

O W

bu

6
w
w
o
.c

to

lic

lic

to

bu

N
w

O W
.d o
c u -tr a c k

.d o

c u -tr a c k

.c

Version 1:
MoveTower(int numDisks, string start, string dest, string temp) { if (numDisks > 0) { MoveTower(numDisks - 1, start, temp, dest); MoveDisk(start, dest); MoveTower(numDisks - 1, temp, dest, start); } }

Correct

Incorrect

Version 2:
MoveTower(int numDisks, string start, string dest, string temp) { if (numDisks > 0) { MoveTower(numDisks - 2, start, temp, dest); MoveDisk(start, dest); MoveDisk(start, dest); MoveTower(numDisks - 2, temp, dest, start); } } MoveTower(int numDisks, string start, string dest, string temp) { if (numDisks > 0) { MoveTower(numDisks - 2, start, dest, temp); MoveDisk(start, temp); MoveDisk(start, temp); MoveTower(numDisks - 2, dest, start, temp); MoveDisk(temp, dest); MoveDisk(temp, dest); MoveTower(numDisks - 2, start, dest, temp); } } MoveTower(int numDisks, string start, string dest, string temp) { if (numDisks > 0) { MoveTower(numDisks - 2, start, dest, temp); MoveDisk(start, temp); MoveTower(numDisks - 2, dest, start, temp); MoveDisk(temp, dest); MoveTower(numDisks - 2, start, temp, dest); MoveDisk(start, dest); MoveTower(numDisks - 2, temp, dest, start); } }

Simply imagine the white disks as being the slightest bit smaller than their dark grey cousins. Then we have the same type of tower as we dealt with in class (to see this, forget about the fact that the disks are colored.

Correct

Incorrect

This will not work. Even if we trust the recursion to move the tower of height numdisks - 2 correctly, it should be clear that the largest white disk will sit on the bottom after the two calls to MoveDisk are complete.

Version 3:

Correct

Incorrect

Again, make that leap, and trust the recursion. Because the two bottom disks are moved in phases, their final orientation is exactly as it was originally. Try this with 4 disks and youll see.

Version 4:

Correct
Incorrect!

Incorrect

w
w

PD

PD

F-

XC

h a n g e Vi e

F-

XC

h a n g e Vi e

er

er

O W

bu

7
w
w
o
.c

to

lic

lic

to

bu

N
w
.d o
c u -tr a c k

O W

.d o

c u -tr a c k

.c

Consider the similar problem of moving yet another type of tower from the leftmost needle to the rightmost needle. Note the color scheme of the tower on the right is different; this time its inverted.

Again, note that each size is represented twice: once as white and once as dark grey. Using the same MoveDisk utility we used above, write a recursive procedure which provides the list of moves needed to move a tower of height numDisks from one peg to another. You must ensure that: only one disk is moved at a time no disk is ever placed on top of a smaller disk the alternating color scheme of the initial tower is inverted in the final tower that means white on the bottom, and then grey, white, grey, white, grey, etc.

void InvertTower(int numDisks, string start, string dest, string temp);

w
w

PD

PD

F-

XC

h a n g e Vi e

F-

XC

h a n g e Vi e

er

er

O W

bu

8
w
w
o
.c

to

lic

lic

to

bu

N
w

O W
.d o
c u -tr a c k

.d o

c u -tr a c k

.c

Problem 6: Twiddles Two English words are considers to be twiddles whenever the letters at each position are either the same, neighboring letters (meaning next to each other in the alphabet), or nextto-neighboring letters. For instance, sparks and snarls are twiddles. Their second and second-to-last characters are different, but p is just two past n in the alphabet, and k comes just before l. A more dramatic example: craggy and eschew. They have no letters in common, but craggys c, r, a, g, g, and y are -2, -1, -2, -1, 2, and 2 away from the e, s, c, h, e, and w in eschew. And just to be clear, a and z are not next to each other in the alphabettheres no wrapping around at all. Write a recursive procedure called ListTwiddles, which accepts a string str and a reference to an English language Lexicon, and prints out all those English words that just happen to be strs twiddles. Youll probably want to write a wrapper function. (Note: any word is considered to be a twiddle of itself, so its okay to print it.)
void ListTwiddles(string str, Lexicon& lex);

w
w

PD

PD

F-

XC

h a n g e Vi e

F-

XC

h a n g e Vi e

er

er

O W

bu

to

lic

lic

to

bu

N
.c

O W
w
.d o
c u -tr a c k

.d o

c u -tr a c k

.c

CS106X Autumn 2010

Handout 15S October 6th 8th, 2010

Section Solution
Solution 1: URL Parameter Map
Map<string> extractQueryMap(string url) { Map<string> parameters; int index = url.find('?'); if (index == string::npos) return parameters; string query = url.substr(index + 1); index = query.find('#'); if (index != string::npos) { query = query.substr(0, index); // chop off hash } Vector<string> pairs = Explode(query, '&'); for (int i = 0; i < pairs.size(); i++) { Vector<string> components = Explode(pairs[i], '='); string key = components[0]; string value = "true"; if (components.size() > 1 && !components[1].empty()) { value = components[1]; } parameters[key] = value; } return parameters; }

Solution 2: Publishing Stories Theres the one-pass approach that just appends characters from the template to the running story, unless that character is '{', in which case we extract everything up through the matching '}' and substitute it with a string from the data map.
string SubstituteTokens(string storyTemplate, Map<string>& data) { string story; for (int i = 0; i < storyTemplate.size(); i++) { if (storyTemplate[i] != '{') { story += storyTemplate[i]; } else { int j = storyTemplate.find('}', i + 1); string token = storyTemplate.substr(i + 1, j - i - 1); story += data.get(token); i = j; } } return story; }

w
w

w
w

PD

PD

F-

XC

h a n g e Vi e

F-

XC

h a n g e Vi e

er

er

O W

bu

2
w
w
o
.c

to

lic

lic

to

bu

N
w

O W
.d o
c u -tr a c k

.d o

c u -tr a c k

.c

Another approach is to iterate over the data map and drive the substitution that way. Its less efficient, but its another way to think about the problem and is a perfectly acceptable answer for the purposes of a discussion section.
string SubstituteOneToken(string story, string token, string value) { int start = 0; while (true) { int found = story.find(token); if (found == string::npos) return story; story.replace(found, token.size(), value); start = found + value.size() + 1; } } string SubstituteTokens(string storyTemplate, Map<string>& data) { string story = storyTemplate; foreach (string token in data) { story = SubstituteOneToken(story, '{' + token + '}', data.get(token)); } return story; }

Solution 3: Chain Reactions As with all nontrivial problems, there are several reasonable approaches. But because you needed to compute what subset of the land mines exploded at second 1, then second 2, etc, some variation of breadth first search is almost certainly required.
int ComputeScore(location init, Set<location>& landmines) { int score = 0; int second = 0; Set<location> notYetExploded = landmines; notYetExploded.remove(init); Set<location> currentlyExploding(LocationCompare); currentlyExploding.add(init); while (!currentlyExploding.isEmpty()) { score += 100 * currentlyExploding.size() * second * second; Set<location> soonExploding(LocationCompare); foreach (location explodingLocation in currentlyExploding) { foreach (location potentialLocation in notYetExploded) { if (IsInRange(explodingLocation, potentialLocation)) { soonExploding.add(potentialLocation); } } } notYetExploded.subtract(soonExploding); currentlyExploding = soonExploding; second++; }

w
w

PD

PD

F-

XC

h a n g e Vi e

F-

XC

h a n g e Vi e

er

er

O W

bu

3
w
w
o
.c

to

lic

lic

to

bu

N
w
.d o
c u -tr a c k

O W

.d o

c u -tr a c k

.c

return score; } location OptimalInitLocation(Set<location>& landmines) { int bestScore = -1; location bestLocation; foreach (location mine in landmines) { int score = ComputeScore(mine, landmines); if (score > bestScore) { bestScore = score; bestLocation = mine; } } return bestLocation; }

Solution 4: Making Change


int MakeChange(int amount, Vector<int>& denominations, int start) { if (amount == 0) return 0; if (amount < 0) return 1; if (start == denominations.size()) return -1; int minCoins = -1; int minCoinsOfRestWith = MakeChange(amount denominations[start], denominations, start); if (minCoinsOfRestWith != -1) minCoins = 1 + minCoinsOfRestWith; int minCoinsWithout = MakeChange(amount, denominations, start + 1); if ((minCoins == -1) || (minCoinsWithout != -1 && minCoinsWithout < minCoins)) minCoins = minCoinsWithout; return minCoins; } int MakeChange(int amount, Vector<int>& denominations) { return MakeChange(amount, denominations, 0); }

w
w

PD

PD

F-

XC

h a n g e Vi e

F-

XC

h a n g e Vi e

er

er

O W

bu

4
w
w
o
.c

to

lic

lic

to

bu

N
w

O W
.d o
c u -tr a c k

.d o

c u -tr a c k

.c

Solution 5: Towers Of Hanoi Revisited Version 1:


MoveTower(int numDisks, string start, string dest, string temp) { if (numDisks > 0) { MoveTower(numDisks - 1, start, temp, dest); MoveDisk(start, dest); MoveTower(numDisks - 1, temp, dest, start); } }

Correct

Incorrect

Version 2:
MoveTower(int numDisks, string start, string dest, string temp) { if (numDisks > 0) { MoveTower(numDisks - 2, start, temp, dest); MoveDisk(start, dest); MoveDisk(start, dest); MoveTower(numDisks - 2, temp, dest, start); } }

Simply imagine the white disks as being the slightest bit smaller than their dark grey cousins. Then we have the same type of tower as we dealt with in class (to see this, forget about the fact that the disks are shaded different colors.)

Correct

Incorrect

Version 3:
MoveTower(int numDisks, string start, string dest, string temp) { if (numDisks > 0) { MoveTower(numDisks - 2, start, dest, temp); MoveDisk(start, temp); MoveDisk(start, temp); MoveTower(numDisks - 2, dest, start, temp); MoveDisk(temp, dest); MoveDisk(temp, dest); MoveTower(numDisks - 2, start, dest, temp); } } MoveTower(int numDisks, string start, string dest, string temp) { if (numDisks > 0) { MoveTower(numDisks - 2, start, dest, temp); MoveDisk(start, temp); MoveTower(numDisks - 2, dest, start, temp); MoveDisk(temp, dest); MoveTower(numDisks - 2, start, temp, dest); MoveDisk(start, dest); MoveTower(numDisks - 2, temp, dest, start); } }

This will not work, as it should be clear that the largest white disk will be on bottom after the two calls to MoveDisk are complete. However, the even number of recursive calls will ensure that all pairs except the bottom pair will maintain their original order.

Correct

Incorrect

Again, make that leap, and trust the recursion. Because the two bottom disks are moved in phases, their final orientation is exactly as it was originally. Try this with 4 disks and youll see. (This is really just like Version 1, except that each level here does the work of two levels there.)

Version 4:

Correct

Incorrect

Notice that the bottom two disks are inverted, so even if the recursion were to somehow work, the bottom two disks would be placed incorrectly. The actual end result is precisely the same as that of version 2.

w
w

PD

PD

F-

XC

h a n g e Vi e

F-

XC

h a n g e Vi e

er

er

O W

bu

5
w
w
o
.c

to

lic

lic

to

bu

N
w

O W
.d o
c u -tr a c k

.d o

c u -tr a c k

.c

void InvertTower(int numDisks, string start, string dest, string temp) { if (numDisks > 0) {; InvertTower(numDisks 2, start, temp, dest); MoveDisk(start, dest); MoveDisk(start, dest); // these two calls invert the largest two InvertTower(numDisks 2, temp, start, dest); InvertTower(numDisks 2, start, dest, temp); } }
}

I suspect this implementation will surprise a good number of you. But if the recursive call really does invert the tower atop the bottom two disks, you need an odd number of recursive calls to invert the tower. Solution 6: Twiddles Heres one implementation that keeps track of which characters have been twiddled so far, and which ones have yet to be. The wrapper function includes a third parameter called position to mark the dividing line between whats already being handled by an active recursive call, and which characters have yet to be changed.
void ListTwiddles(string str, Lexicon& lex) { ListTwiddles(str, 0, lex); } void ListTwiddles(string str, int position, Lexicon& lex) { if (position == str.length()) { if (lex.containsWord(str)) { cout << str << endl; } return; } char saved = str[position]; for (int delta = -2; delta <= 2; delta++) { char ch = saved + delta; if (islower(ch)) { // islower technically not needed str[position] = ch; ListTwiddles(str, position + 1, lex); } } }

w
w

PD

PD

F-

XC

h a n g e Vi e

F-

XC

h a n g e Vi e

er

er

O W

bu

6
w
w
o
.c

to

lic

lic

to

bu

N
w

O W
.d o
c u -tr a c k

.d o

c u -tr a c k

.c

Another approach could work like the permutations and the subsets examples we covered in class. Maintain a working prefix and recursively extend that prefix in one of five ways:
void ListTwiddles(string str, Lexicon& lex) { ListTwiddles("", str, lex); } void ListTwiddles(string prefix, string remaining, Lexicon& lex) { if (!lex.containsPrefix(prefix)) { return; } if (remaining == "") { if (lex.containsWord(prefix)) { cout << prefix << endl; } return; } for (int delta = -2; delta <= 2; delta++) { char ch = remaining[0] + delta; if (islower(ch)) { ListTwiddles(prefix + ch, remaining.substr(1), lex); } } }

The islower calls arent technically necessary. Its fine to generate strings with nonalphabetic characters in them, because theyll just fail the containsWord test every time.

w
w

PD

PD

F-

XC

h a n g e Vi e

F-

XC

h a n g e Vi e

er

er

O W

bu

to

lic

lic

to

bu

N
.c

O W
w
.d o
c u -tr a c k

.d o

c u -tr a c k

.c

CS106X Autumn 2011

Handout 16 October 10th, 2011

Section Handout
Discussion Problem 1: Publishing Stories Social networking sites like Facebook, LinkedIn, Orkut, Friendster, and MySpace typically record and publish stories about actions taken by you and your friends. Stories such as: John Dixon accepted your friend request. Jeff Barbose is no longer in a relationship. Scott James wrote a note called "The Two Percent Solution". Arlene Heitner commented on Melodie Bowshers video. Antonio Melara gave The French Laundry a 5-star review. are created from story templates like {name} accepted your friend request. {name} is no longer in a relationship. {name} wrote a note called "{title}". {name} commented on {target}s video. {actor} gave {restaurant} a {rating}-star review. The specific story is generated from the skeletal one by replacing the tokenssubstrings like "{name}", "{title}", and "{rating}"with event-specific values, like "John Dixon", "The Two Percent Solution", and "5". The token-value pairs can be packaged in a Map<string>, and given a story template and a data map, its possible to generate an actual story. Write the substituteTokens function, which accepts a story template (like "{actor} gave {restaurant} a {rating}-star review.") and a Map<string> (which might map "actor" to "Antonio Melara", "restaurant" to "The French Laundry", and "rating" to "5"), and builds a string just like the story template, except that the tokens have been replaced by the text they map to. Assume the following is true: '{' and '}' exist to delimit token names, but wont appear anywhere else. In other words, if you encounter the '{' character, you can assume it marks the beginning of a token that ends with a '}'. We guarantee that all tokens are in the Map<string>. You dont need to do any error checking. Note that the '{' and the '}' arent included in the map. "{name}", for instance, is replaced with whatever "name" identifies in the map.

w
w

w
w

PD

PD

F-

XC

h a n g e Vi e

F-

XC

h a n g e Vi e

er

er

O W

bu

2
w
w
o
.c

to

lic

lic

to

bu

N
w
.d o
c u -tr a c k

O W

.d o

c u -tr a c k

.c

The prototype is:


string substituteTokens(string storyTemplate, Map<string>& data);

Discussion Problem 2: Superwords A superword is an English word where every prefix and every suffix is also a word (assuming that all the single letters"a", "b", "c", etc.are English words). For instance, "amuser" is a superword, because all prefixes ("a", "am", "amu", "amus", and "amuse") and all suffixes ("muser", "user", "ser", "er", and "r") are English words as well. Neat! Implement the collectLongestSuperwords, which given a reference to a Lexicon, returns all of the longest superwords as a Vector<string>. Your approach: You should iterate over the entire Lexicon, deciding whether each word is a superword, and if its among the longest of superwords youve seen so far, you should add it to the Vector<string>. [You should only return the longest superwords, so that all strings in the return Vector<string> are of the same length.]
Vector<string> collectLongestSuperwords(Lexicon& english);

Lab Problem 1: URL Parameter Map A URL has many components. a protocol, a domain, and a file path are almost always included. Sometimes the URL includes a query, which the portion of the URL that appears after the ? and (if present) before the # (which defines the URLs hash). Here are some examples: http://www.google.com/ig?hl=en&source=iglk http://www.facebook.com/profile.php?id=1160&viewas=214707 http://store.apple.com/us/browse/family/iphone?mco=MTAyNTM5ODU

And while you may recognize the ? as a common character within URLs, you may not realize that the part following the ? is the serialization of a Map<string>. The query string is an &delimited string of key-value pairs, where each key-value pair takes the form <key>=<value>. When a web server gets an HTTP request, it digests the query string and re-hydrates it into Map<string> form, and uses that map to programmatically shape how the response page is generated. Thats why www.google.com/ig?hl=en&source=iglk generates English, and www.google.com/ig?hl=fr&source=iglk generates French. One little wrinkle: the value is technically optional, so that query strings like type=5&seeall and source_id=9074&read= are legitimate. When a key is present without a value, then the value is arbitrary and the presence of the key is the only thing of interest. In such cases, the Map<string> should include the key and attach an arbitrary value to it.

w
w

PD

PD

F-

XC

h a n g e Vi e

F-

XC

h a n g e Vi e

er

er

O W

bu

3
w
w
o
.c

to

lic

lic

to

bu

N
w
.d o
c u -tr a c k

O W

.d o

c u -tr a c k

.c

Write a function that takes a legitimate URL, extracts the query string, and returns a Map<string> with all of its key-value pairs. You can safely assume that the URL is properly formatted, and that the structure of an URL is no more complicated than whats described here. Make sure you deal with URLs that dont include a query string, and URLs that include a hash.
Map<string> extractQueryMap(string url);

This problem is as much about string manipulation as it is about the Map, so dont be surprised if youre using some advanced string methods that havent come up in any previous examples. Ensure that: substrings of the form <key>=<value> contribute a relevant key-value pair to the map when <value> is one or more characters long. substrings of the form <key>= and key contribute the relevant key and an arbitrary value to the map. you ignore anything beyond the optional hash, the start of which is marked by '#'. So: o http://www.google.com/mail/?shva=1&hl=en, and o http://www.google.com/mail/?shva=1&hl=en#spam have the same parameter map. The lab code has been set up as a unit test that exercises your extractQueryMap function to confirm that its working in a variety of situations. Youre to complete the implementation of extractQueryMap so that all unit tests pass. Ive included my implementation of an explode function that does some automatic tokenizing around the provided delimiter character. Youll be able to figure it out. Lab Problem 2: Chain Reactions Chain Reaction is a popular one-player game making its way through the Internet and various mobile platforms. In our version, weve given a collection of locations(x,y) coordinates in the planeof land mines. The detonation of any single land mine prompts all land mines within a certain distance to simultaneously detonate a second later, which themselves set off more land mines another second later, and so forth, and so forth. The chain reaction continues until there are no active land mines, or until none of the remaining land mines happen to fall within the threshold distance of those that have exploded.

w
w

PD

PD

F-

XC

h a n g e Vi e

F-

XC

h a n g e Vi e

er

er

O W

bu

4
w
w
o
.c

to

lic

lic

to

bu

N
w

O W
.d o
c u -tr a c k

.d o

c u -tr a c k

.c

You get 0 points for the land mine you initially [and manually] detonate. You get 100 points for each land mine that detonates at the one-second mark. You get 400 points for each land mine that detonates at the two-second mark. You get 900 points for each land mine that detonates at the three-second mark. In general, you get 100n2 points for each land mine that detonates at the n-second mark.

Implement the core of the Chain Reaction game, as described above. The user gets to select a land mine that prompts the chain reactionoptimal or notand computes the score. The user is then prompted with another set of mines, and then another, and then another, until the user quits the application. Ive included a good amount of starter code that implements most of the game, save for the actual sweep that emulates the chain reaction. Ive also included a sample application as well, so you know how the starter code and the final solution differ.

w
w

PD

PD

F-

XC

h a n g e Vi e

F-

XC

h a n g e Vi e

er

er

O W

bu

to

lic

lic

to

bu

N
.c

O W
w
.d o
c u -tr a c k

.d o

c u -tr a c k

.c

CS106X Autumn 2011

Handout 16S October 12th 14th, 2011

Section Solution
Discussion Problem Solution 1: Publishing Stories Theres the one-pass approach that just appends characters from the template to the running story, unless that character is '{', in which case we extract everything up through the matching '}' and substitute it with a string from the data map.
string substituteTokens(string storyTemplate, Map<string>& data) { string story; for (int i = 0; i < storyTemplate.size(); i++) { if (storyTemplate[i] != '{') { story += storyTemplate[i]; } else { int j = storyTemplate.find('}', i + 1); string token = storyTemplate.substr(i + 1, j - i - 1); story += data.get(token); i = j; } } return story; }

Another approach is to iterate over the data map and drive the substitution that way. Its less efficient, but its another way to think about the problem and is a perfectly acceptable answer for the purposes of a discussion section.
string substituteOneToken(string story, string token, string value) { int start = 0; while (true) { int found = story.find(token); if (found == string::npos) return story; story.replace(found, token.size(), value); start = found + value.size() + 1; } } string substituteTokens(string storyTemplate, Map<string>& data) { string story = storyTemplate; foreach (string token in data) { story = substituteOneToken(story, '{' + token + '}', data.get(token)); } return story; }

w
w

w
w

PD

PD

F-

XC

h a n g e Vi e

F-

XC

h a n g e Vi e

er

er

O W

bu

2
w
w
o
.c

to

lic

lic

to

bu

N
w

O W
.d o
c u -tr a c k

.d o

c u -tr a c k

.c

Discussion Problem Solution 2: Superwords


bool isSuperWord(string word, Lexicon& english) { for (int i = 1; i < word.size(); i++) { if (!english.containsWord(word.substr(0, i))) { return false; } } for (int i = 1; i < word.size(); i++) { if (!english.containsWord(word.substr(i))) { return false; } } return true; } Vector<string> collectLongestSuperwords(Lexicon& english) { int length = 0; Vector<string> longestSuperwords; foreach (string word in english) { if (isSuperWord(word, english) && word.size() >= length) { if (word.size() > length) { longestSuperwords.clear(); length = word.size(); } longestSuperwords.add(word); } } return longestSuperwords; }

Lab Problem Solution 1: URL Parameter Map The core of what needs to be written should be consistent with the implementation of extractQueryMap that Im providing below. (The explode function is provided as part of the lab starter code.)
Map<string> extractQueryMap(string url) { Map<string> parameters; int index = url.find('?'); if (index == string::npos) return parameters; string query = url.substr(index + 1); index = query.find('#'); if (index != string::npos) { query = query.substr(0, index); // chop off hash } Vector<string> pairs = explode(query, '&'); for (int i = 0; i < pairs.size(); i++) { Vector<string> components = explode(pairs[i], '='); string key = components[0];

w
w

PD

PD

F-

XC

h a n g e Vi e

F-

XC

h a n g e Vi e

er

er

O W

bu

3
w
w
o
.c

to

lic

lic

to

bu

N
w

O W
.d o
c u -tr a c k

.d o

c u -tr a c k

.c

string value = "true"; if (components.size() > 1 && !components[1].empty()) { value = components[1]; } parameters[key] = value; } return parameters; }

Lab Problem Solution 2: Chain Reactions Heres the core of my solution, with the code for the graphics and animation removed.
int computeScore(location init, Set<location>& landmines) { int score = 0; int second = 0; Set<location> notYetExploded = landmines; notYetExploded.remove(init); Set<location> currentlyExploding(LocationCompare); currentlyExploding.add(init); while (!currentlyExploding.isEmpty()) { score += 100 * currentlyExploding.size() * second * second; Set<location> soonExploding(LocationCompare); foreach (location explodingLocation in currentlyExploding) { foreach (location potentialLocation in notYetExploded) { if (isInRange(explodingLocation, potentialLocation)) { soonExploding.add(potentialLocation); } } } notYetExploded.subtract(soonExploding); currentlyExploding = soonExploding; second++; } return score; }

w
w

PD

PD

F-

XC

h a n g e Vi e

F-

XC

h a n g e Vi e

er

er

O W

bu

to

lic

lic

to

bu

N
.c

O W
w
.d o
c u -tr a c k

.d o

c u -tr a c k

.c

CS106B Winter 03-04

Handout #12 January 21, 04

Section Handout #2
Problem 1: Old-Fashioned Measuring (Chapter 5, Programming Exercise 10) I am the only child of parents who weighed, measured, and priced everything; for whom what could not be weighed, measured, and priced had no existence. Charles Dickens, Little Dorrit, 1857 In Dickenss time, merchants measured many commodities using weights and a two-pan balance a practice that continues in many parts of the world today. If you are using a limited set of weights, however, you can only measure certain quantities accurately. For example, suppose that you have only two weights: a 1-ounce weight and a 3-ounce weight. With these you can easily measure out 4 ounces, as shown:

It is somewhat more interesting to discover that you can also measure out 2 ounces by shifting the 1-ounce weight to the other side, as follows:

Write a recursive function


bool IsMeasurable(int target, int weights[], int nWeights)

that determines whether it is possible to measure out the desired target amount with a given set of weights. The available weights are stored in the array weights, which has nWeights as its effective size. For instance, the sample set of two weights illustrated above could be represented using the following pair of variables:
int sampleWeights[] = { 1, 3 }; int nSampleWeights = 2;

w
w

w
w

PD

PD

F-

XC

h a n g e Vi e

F-

XC

h a n g e Vi e

er

er

O W

bu

lic

lic

2
w
.c

to

to

bu

N
w

O W
.d o
c u -tr a c k

.d o

c u -tr a c k

.c

Given these values, the function call


IsMeasurable(2, sampleWeights, nSampleWeights)

should return true because it is possible to measure out 2 ounces using the sample weight set as illustrated in the preceding diagram. On the other hand, calling
IsMeasurable(5, sampleWeights, nSampleWeights)

should return false because it is impossible to use the 1- and 3-ounce weights to add up to 5 ounces. The fundamental observation you need to make for this problem is that each weight in the array can be either: Put on the opposite side of the balance from the sample Put on the same side of the balance as the sample Left off the balance entirely If you consider one of the weights in the array and determine how choosing one of these three options affects the rest of the problem, you should be able to come up with the recursive insight you need to solve the problem. Problem 2: Subsets of a String One interesting problem that has a recursive solution is the generation of all possible subsets of a given set. For example, given the set {ABC}, it is useful to have a program that generates the following sets:
{ABC}, {AB}, {AC}, {BC}, {A}, {B}, {C}, {}

The subset problem has a recursive formulation. If you represent a set of characters using a string that either contains or does not contain a given letter, you can calculate all possible subsets by; (1) including the first character in the subset and concatenating it onto the front of all subsets of the remaining N1 characters and then (2) just printing out the subsets of the remaining N1 without this character. Write a program ListSubsets that prints out all subsets of a given set, represented as a string of characters. You may assume that the string contains no whitespace or duplicated letters. Thus, the call ListSubsets("ABC") should produce
{ { { { { { { { A A A A B B C } B B C } C } } C } } } }

w
w

PD

PD

F-

XC

h a n g e Vi e

F-

XC

h a n g e Vi e

er

er

O W

bu

lic

lic

3
w
.c

to

to

bu

N
w

O W
.d o
c u -tr a c k

.d o

c u -tr a c k

.c

Problem 3: Shortest Path Through a Maze In many mazes, there are multiple paths. For example, the diagrams below show three solutions for the same maze:

le n g t h = 1 3

le n g t h = 1 5

le n g t h = 1 3

None of these solutions, however, is optimal. The shortest path through the maze has a path length of 11:

As a starting point, begin by considering the general maze solution covered in the reader:
/* * Function: SolveMaze * Usage: if (SolveMaze(pt)) . . . * * This function attempts to generate a solution to the current * maze from point pt. SolveMaze returns true if the maze has * a solution and false otherwise. The implementation uses * recursion to solve the sub-mazes that result from marking the * current square and moving one step along each open passage. */ static bool SolveMaze(pointT pt) { if (OutsideMaze(pt)) return true; if (IsMarked(pt)) return false; MarkSquare(pt); for (directionT dir = North; dir <= West; dir = directionT(dir + 1)) { if (!WallExists(pt, dir)) { if (SolveMaze(AdjacentPoint(pt, dir))) { return true; } } } UnmarkSquare(pt);

w
w

PD

PD

F-

XC

h a n g e Vi e

F-

XC

h a n g e Vi e

er

er

O W

bu

lic

lic

4
w
.c

to

to

bu

N
w

O W
.d o
c u -tr a c k

.d o

c u -tr a c k

.c

return false; }

w
w

PD

PD

F-

XC

h a n g e Vi e

F-

XC

h a n g e Vi e

er

er

O W

bu

lic

lic

5
w
.c

to

to

bu

N
w

O W
.d o
c u -tr a c k

.d o

c u -tr a c k

.c

Write a function
int ShortestPathLength(pointT pt);

that returns the length of the shortest path in the maze from the specified position to any exit. If there is no solution to the maze, ShortestPathLength should return the constant NoSolution, which is defined to have a value larger than the maximum permissible path length, as follows:
#define NoSolution 10000

Problem 4: Filling A Region Most drawing programs for personal computers make it possible to fill an enclosed region on the screen with a solid color. Typically, you invoke this operation by selecting a paint-bucket tool and then clicking the mouse, with the cursor somewhere in your drawing. When you do, the paint spreads to every part of the picture it can reach without going through a line. For example, suppose you have just drawn the following picture of a house:

If you select the paint bucket and click inside the door, the drawing program fills the area bounded by the doorframe as shown at the left side of the following diagram. If you instead click somewhere on the front wall of the house, the program fills the entire wall space except for the windows and doors, as shown on the right:

w
w

PD

PD

F-

XC

h a n g e Vi e

F-

XC

h a n g e Vi e

er

er

O W

bu

lic

lic

6
w
.c

to

to

bu

N
w

O W
.d o
c u -tr a c k

.d o

c u -tr a c k

.c

In order to understand how this process works, it is important to understand that the screen of the computer is actually broken down into an array of tiny dots called pixels. On a monochrome display, pixels can be either white or black. The paint-fill operation consists of painting black the starting pixel (i.e., the pixel you click while using the paint-bucket tool) along with any pixels connected to that starting point by an unbroken chain of white pixels. Thus, the patterns of pixels on the screen representing the preceding two diagrams would look like this:

Write a program that simulates the operation of the paint-bucket tool. To simplify the problem, assume that you have access to the enumerated type
enum pixelStateT { White, Black };

The type pointT is defined in Chapter 6 as follows:


struct pointT { int x, y; };

Assume you also have the following functions:


pixelStateT GetPixelState(pointT pt); void SetPixelState(pointT pt, pixelStateT state); bool OutsidePixelBounds(pointT pt);

w
w

PD

PD

F-

XC

h a n g e Vi e

F-

XC

h a n g e Vi e

er

er

O W

bu

lic

lic

7
w
.c

to

to

bu

N
w

O W
.d o
c u -tr a c k

.d o

c u -tr a c k

.c

The first function is used to return the state of any pixel, given its coordinates in the pixel array. The second function allows you to change the state of any pixel to a new value. The third makes it possible to determine whether a particular coordinate is outside the pixel array altogether, so that the recursion can stop at the edges of the screen. Your task in this problem is therefore to write a function
static void FillRegion(pointT pt);

that fills black into all white pixels reachable from the point pt.

w
w

PD

PD

F-

XC

h a n g e Vi e

F-

XC

h a n g e Vi e

er

er

O W

bu

lic

lic

8
w
.c

to

to

bu

N
w

O W
.d o
c u -tr a c k

.d o

c u -tr a c k

.c

Problem 5: Edit Distance* In 1965, Russian scientist Vladimir Levenshtein devised the notion of "Levenshtein distance" to measure the similarity between strings. The distance between two identical strings is 0, otherwise it is the minimum number of deletions, insertions, or substitutions required to transform one string into the other. For example, you can transform "happy" into "sappy" with just one substitution (change 's' to 'h') so the distance is 1. Changing "bite" into "built" requires two insertions and one deletion, so the distance is 3. The greater the Levenshtein distance between two strings, the more dissimilar the strings are. The Levenshtein distance algorithm can be used for spell checking, speech recognition, DNA analysis, and more. Write a function to compute the Levenshtein distance between two given strings:
int EditDistance (string s, string t);

The two parameters are the source and target strings. An "edit" is substituting one character for another, deleting a character, or inserting a character. The function returns the minimum number of edits required to transform the source string into the target string. You dont need to change the source or target string or allocate additional strings. * If you can't spell or pronounce Levenshtein, the metric is also sometimes called edit distance.

w
w

PD

PD

F-

XC

h a n g e Vi e

F-

XC

h a n g e Vi e

er

er

O W

bu

to

lic

lic

to

bu

N
.c

O W
w
.d o
c u -tr a c k

.d o

c u -tr a c k

.c

CS106B Winter 03-04

Handout #12S January 21, 04

Section Solutions #2
Problem 1: Old-Fashioned Measuring (Chapter 5, Programming Exercise 10)
static bool IsMeasurable(int target, int weights[], int nWeights) { if (target == 0) return true; if (nWeights <= 0) return false; return ( IsMeasurable(target + weights[0], weights + 1, nWeights - 1)

|| IsMeasurable(target, weights + 1, nWeights - 1) || IsMeasurable(target - weights[0], weights + 1, nWeights - 1)); }

Problem 2: Subsets of a String


/* * Function: SubsetsWithFixedPrefix * Usage: SubsetsWithFixedPrefix(prefix, rest); * -------------------------------------------* This function implements the recursive subset algorithm. * In English, this function corresponds to the imperative * statement: Generate and print all subsets of the string * str, holding the first k characters fixed. The recursive * insight is that the subsets either include the character * in position str[k] or they don't. To indicate that a * character should not be included, the implementation * either will add that character to the fixed prefix string, * or it will leave it out. At the same time, we remove the * string from the rest, so that the problem will get smaller. */ static void SubsetsWithFixedPrefix(string prefix, string rest) { if (rest == "") { cout << "{" << prefix << "}" << endl; } else { SubsetsWithFixedPrefix(prefix + rest[0], rest.substr(1)); SubsetsWithFixedPrefix(prefix, rest.substr(1)); }

w
w

w
w

PD

PD

F-

XC

h a n g e Vi e

F-

XC

h a n g e Vi e

er

er

O W

bu

lic

lic

2
w
.c

to

to

bu

N
w

O W
.d o
c u -tr a c k

.d o

c u -tr a c k

.c

} /* Function: ListSubsets * Usage: ListSubsets(str) * ---------------------------* This function lists all combinations of the characters * in the string str. */ static void ListSubsets(string str) { SubsetsWithFixedPrefix("", str); }

Problem 3: Shortest Path Through a Maze


static int ShortestPathLength(pointT pt) { int shortest, len; if (OutsideMaze(pt)) return (0); if (IsMarked(pt)) return (NoSolution); shortest = NoSolution; MarkSquare(pt); for (directionT dir = North; dir <= West; dir = directionT(dir + 1)) { if (!WallExists(pt, dir)) { len = ShortestPathLength(AdjacentPoint(pt, dir)); if (len < shortest) shortest = len; } } UnmarkSquare(pt); if (shortest == NoSolution) return (NoSolution); else return (shortest + 1); }

w
w

PD

PD

F-

XC

h a n g e Vi e

F-

XC

h a n g e Vi e

er

er

O W

bu

lic

lic

3
w
.c

to

to

bu

N
w

O W
.d o
c u -tr a c k

.d o

c u -tr a c k

.c

Problem 4: Filling A Region


/* * Function: FillRegion * Usage: FillRegion(pt); * ---------------------* This function paints black pixels everywhere inside the * region surrounding the point. */ static void FillRegion(pointT pt) { if (OutsidePixelBounds(pt)) return; if (GetPixelState(pt) == Black) return; SetPixelState(pt, Black); FillRegion(AdjacentPoint(pt, FillRegion(AdjacentPoint(pt, FillRegion(AdjacentPoint(pt, FillRegion(AdjacentPoint(pt, }

North)); East)); South)); West));

Problem 5: Edit Distance


int Min(int a, int b, int c) { if (a < b) { if (a < c) return a; else return c; } else { if (b < c) return b; else return c; } } int EditDistance(string s, string t) { if (s == t) return 0; if (s == "") return t.length(); if (t == "") return s.length(); if (s[0] == t[0]) // found match, recur on rest return EditDistance(s.substr(1), t.substr(1)); else return 1 + Min( EditDistance (s.substr(1), t.substr(1)), // substitute first of t for first of s EditDistance (s.substr(1), t), // delete first char of s EditDistance (s, t.substr(1)) ); // insert first char of t into s }

w
w

PD

PD

F-

XC

h a n g e Vi e

F-

XC

h a n g e Vi e

er

er

O W

bu

to

lic

lic

to

bu

N
.c

O W
w
.d o
c u -tr a c k

.d o

c u -tr a c k

.c

CS106B Winter 04-05

Handout #13 January 19, 2005

Section Handout #2
Problem 1: Cannonballs Suppose that you have somehow been transported back to 1777 and the Revolutionary War. You have been assigned a dangerous reconnaissance mission: evaluate the amount of ammunition available to the British for use with their large cannon which has been shelling the Revolutionary forces. Fortunately for you, the Britishbeing neat and orderlyhave stacked the cannonballs into a single pyramid-shaped stack with one cannonball at the top, sitting on top of a square composed of four cannonballs, sitting on top of a square composed of nine cannonballs, and so forth. Unfortunately, however, the Redcoats are also vigilant, and you only have time to count the number of layers in the pyramid before you are able to escape back to your own troops. To make matters worse, computers will not be invented for at least 150 years, but you should not let that detail get in your way. Your mission is to write a recursive function Cannonball that takes as its argument the height of the pyramid and returns the number of cannonballs therein.
int Cannonball(int height);

Problem 2: ReverseString Given a string, create a function ReverseString that returns the string in reverse order. Consider both recursive and iterative techniques for solving this problem. Which one is easier to come up with?
string ReverseString(string str);

Problem 3: GCD The greatest common divisor (g.c.d.) of two nonnegative integers is the largest integer that divides evenly into both. In the third century B.C., the Greek mathematician Euclid discovered that the greatest common divisor of x and y can always be computed as follows: If x is evenly divisible by y, then y is the greatest common divisor. Otherwise, the greatest common divisor of x and y is always equal to the greatest common divisor of y and the remainder of x divided by y. Use Euclid's insight to write a recursive function int GCD(int x, int y) that computes the greatest common divisor of x and y.

w
w

w
w

PD

PD

F-

XC

h a n g e Vi e

F-

XC

h a n g e Vi e

er

er

O W

bu

lic

lic

2
w
.c

to

to

bu

N
w

O W
.d o
c u -tr a c k

.d o

c u -tr a c k

.c

Problem 4: What kind of structures do you like? You need to store a collection of a particular type of structure. In general, think about the differences between declaring a static array of the structures, a dynamic array of those structures, and a static array of pointers to those structures. In what cases would you want to use each of these approaches? Problem 5: Spot the Bug While debugging some code, you narrow your search down to the following DoubleArrayLength function. Unfortunately, because pointers and arrays are rather difficult to display in a debugger, youre having some trouble finding the bug. You decide instead to trace the function by hand to see if you can spot the bug. Trace through a call to the following function to find the bug. Once you have found the bug, determine what should be done to fix it.
/* The following function doubles the length of an array of integers */ void DoubleArrayLength(int *array, int length) { int *newArray = new int[length * 2]; for (int i = 0; i < length; i++) { newArray[i] = array[i]; } for (int i = length; i < length * 2; i++) { newArray[i] = 0; } array = newArray; } int main() { int *myArray = new int[5]; for (int i = 0; i < 5; i++) { myArray[i] = i; } DoubleArrayLength(myArray, 5); cout << myArray[7]; return 0; }

w
w

PD

PD

F-

XC

h a n g e Vi e

F-

XC

h a n g e Vi e

er

er

O W

bu

lic

lic

3
w
.c

to

to

bu

N
w

O W
.d o
c u -tr a c k

.d o

c u -tr a c k

.c

Problem 6: 2-D Arrays Write a function Make2D that takes a one-dimensional array, a row size, and a column size, and returns a 2-D array from the numbers in the first array. Make sure that your function can deal with arrays of different dimensions. You can also assume that there will be enough data in the array to fill the 2-D array of the specified size (i.e. there will be at least row * col elements in the one-dimensional array) For example, if we provide the following array:

And the row and column dimensions 4 and 4, we should get a pointer to a dynamically allocated array that looks like this:

The prototype is as follows:


double** Make2D(double array[], int rows, int cols);

w
w

PD

PD

F-

XC

h a n g e Vi e

F-

XC

h a n g e Vi e

er

er

O W

bu

to

lic

lic

to

bu

N
.c

O W
w
.d o
c u -tr a c k

.d o

c u -tr a c k

.c

CS106B Winter 04-05

Handout #13S January 20, 2005

Section Solutions #2
Problem 1: Cannonballs
/* * Function: Cannonball * Usage: n = Cannonball(height); * -----------------------------* This function computes the number of cannonballs in a stack * that has been arranged to form a pyramid with one cannonball * at the top sitting on top of a square composed of four * cannonballs sitting on top of a square composed of nine * cannonballs, and so forth. The function Cannonball computes * the total number based on the height of the stack. */ int Cannonball(int height) { if (height == 0) { return (0); } else { return (height * height + Cannonball(height - 1)); } }

Problem 2: ReverseString
string ReverseStringRecursive (string str) { if (str.length() == 0) { return ""; } return ReverseStringRecursive(str.substr(1)) + str[0]; } string ReverseStringIterative (string str) { string result = ""; for (int i = str.length() - 1; i >= 0; i--) { result += str[i]; } return result; }

Which one is easier to come up with? The recursive one, of course!

w
w

w
w

PD

PD

F-

XC

h a n g e Vi e

F-

XC

h a n g e Vi e

er

er

O W

bu

to

lic

lic

to

bu

N
.c

O W
w
.d o
c u -tr a c k

.d o

c u -tr a c k

.c

Problem 3: GCD
int GCD(int x, int y) { if ((x % y) == 0) { return y; } else { return GCD (y, x % y); } }

Problem 4: What kind of structures do you like? Static array: + No dynamic allocation, don't have to work with pointers, can grow/shrink number of elements used (within bounds of the array) - The fixed upper bound means potentially too small or too large, can waste a lot of space or limit utility value + Exactly the size you need, as little or as large - Have to remember to allocate & thus must know size in advance, can't grow/shrink easily once allocated + Moderately conservative in use of memory, can grow/shrink within bounds of array, pointers are easier to swap and move around inside array - Lots of allocations means lots of opportunities to forget, still have fixed upper limit problems, forces you to deal with pointers

Dynamic array: Static array of ptrs:

Any decision among these three choices should consider such factors as the size of the structure itself, how large the variance is in the number of elements needed, how comfortable you feel about working with pointers, whether this is a known upper bound that is never exceeded, whether you need to grow and shrink the array, and so on.

w
w

w
w

PD

PD

F-

XC

h a n g e Vi e

F-

XC

h a n g e Vi e

er

er

O W

bu

to

lic

lic

to

bu

N
.c

O W
w
.d o
c u -tr a c k

.d o

c u -tr a c k

.c

Problem 5: Spot the Bug The bug occurs because myArray is passed into DoubleArrayLength by value rather than by reference. This means that changes to the variable array in DoubleArrayLength are not seen by myArray in main. This seems a little counter intuitive since array is a pointer, and we say that a pointer allows changes to be seen by the caller. What happens is that any changes to elements of the array will be seen, but DoubleArrayLength changes not the elements of the array but where the array is in the first place. Here is a picture, in case that helps: STACK
DoubleArrayLength array 0 1 2 3 4 0 0 0 0 0

HEAP

length

5 main

myArray

Note that the value of myArray itself has not been changed by a call to DoubleArrayLength. To fix this, we do the same thing that we always do to allow a function to change the variables passed to it in a way that the caller sees the changeswe use a reference! All we have to do is change the prototype to DoubleArrayLength to the following and all of our troubles go away:
static void DoubleArrayLength(int *&array, int length)

To help you understand int pointer to an integer.

*&array,

read it from the right. array is a reference to a

w
w

w
w

PD

PD

F-

XC

h a n g e Vi e

F-

XC

h a n g e Vi e

er

er

O W

bu

to

lic

lic

to

bu

N
.c

O W
w
.d o
c u -tr a c k

.d o

c u -tr a c k

.c

Problem 6: 2-D Arrays Note that since C++ uses memory in a row-major format, we start by creating an array of double*'s to hold all the rows, and then fill in the numbers by column.
double** Make2D(double array[], int nRows, int nCols) { double** result; result = new double*[nRows]; for (int i = 0; i < nRows; i++) { result[i] = new double[nCols]; for (int j = 0; j < nCols; j++) { result[i][j] = array[(i * nCols) + j]; } } return result;

w
w

w
w

PD

PD

F-

XC

h a n g e Vi e

F-

XC

h a n g e Vi e

er

er

O W

bu

to

lic

lic

to

bu

N
.c

O W
w
.d o
c u -tr a c k

.d o

c u -tr a c k

.c

CS106B Winter 06-07

Handout #15 January 22, 2007

Section Handout #2
Problem 1: Vectors (AKA C++ ArrayLists) Say we are writing the next version of Eudora and want to use a Vector (interface in reader appendix) to store all the data. The following structure is used to hold the data of an email message:
struct eMailMsg { string to; string from; string message; string subject; int date; int time; }; // // // // // // i.e. i.e. body i.e. date time "professor@stanford.edu" "student@stanford.edu" of message "CS106 Rocks!" email was sent email was sent

a) How would you declare a Vector that stores eMailMsgs? b) Write a function RemoveSpam that takes a vector containing elements of type eMailMsg and removes all elements whose subject begins with the string "SPAM". c) How could you modify the to field of the eMailMsg structure so that it can hold the email addresses of an arbitrary number of recipients of an email? With the modification in place, given an eMailMsg email, how would you access the last address listed in the to field? Problem 2: Queues Write a function
void ReverseQueue(Queue<int> & q);

that reverses the elements in the passed in queue. (Hint: Is there another class that could make doing this a lot easier?) Problem 3: Map Warm-up Write a function:
char MostFrequentCharacter(ifstream & if, int & numOccurrences);

that given an input file stream, returns the character that occurs the most frequently and stores the number of times it occurs in the reference parameter numOccurrences. To write this function, first start by scanning through the file stream, analyzing each character and storing an updated count in a map. Then, after youve built this table, iterate over it to find the character that occurred the most often.

w
w

w
w

PD

PD

F-

XC

h a n g e Vi e

F-

XC

h a n g e Vi e

er

er

O W

bu

lic

lic

2
w
.c

to

to

bu

N
w

O W
.d o
c u -tr a c k

.d o

c u -tr a c k

.c

Problem 4: Crossword Puzzle A crossword puzzle solution is represented as a grid of characters, using an asterisk to indicate blackout squares, as shown in the example below:
[0][0] * * * * * * * M E * N * A N E C R U * N O S H O T * T O S O O T H E S * * M Y * * E * L * * * E * R O A D * A * O C D * * T * T I V O L I * * * A L A N * S Y N E R G Y H O T * C * A A D A G E * C V E G A N * H E L E G Y * T * * * * * * *

You are to write the function AcrossWords that builds a vector containing the words that read across in the puzzle solution. Each sequence of two or more characters bounded by asterisks forms a word. For example, in the puzzle above, the words "MESS", "ROT", and "SHAVE" read across the top row. There will always be an asterisk in the first and last columns of every row of the puzzle. The one parameter to AcrossWords is the grid with the puzzle solution (passed by reference.) The function returns a vector of strings where the vector elements are the words that read across in the order found when processing the puzzle from top-left to bottom-right.
Vector<string> AcrossWords(Grid<char> & puzzle)

The Grid interface is found in the reader's appendix.

w
w

PD

PD

F-

XC

h a n g e Vi e

F-

XC

h a n g e Vi e

er

er

O W

bu

to

lic

lic

to

bu

N
.c

O W
w
.d o
c u -tr a c k

.d o

c u -tr a c k

.c

CS106B Winter 06-07

Handout #15S Janurary 24, 2007

Section Solutions #2
Problem 1: Vectors a) Vector<eMailMsg> mailVector; b)
void RemoveSpam(Vector<eMailMsg> & v) { for (int i = 0; i < v.size(); i++) { eMailMsg mail = v[i]; if (mail.subject.find("SPAM") == 0) { v.removeAt(i); i--; // look at this index again, // since elements have been shifted down } } }

Note that you also could move backwards down the vector and not have to worry about decrementing i. c) We use another Vector, of course!
struct eMailMsg { Vector<string> to; string from; string message; string subject; int date; int time; };

Access to the last element of eMailMsg email would be done by:


string lastAddress = email.to[email.to.size() 1];

Problem 2: Queues
/** * The client version of reverse queue. In order * to change the order of elements in the queue, * we use an external stack */ void ReverseQueue(Queue<int> & queue) { Stack<int> stack; while (!queue.isEmpty()) { stack.push(queue.dequeue()); } while (!stack.isEmpty())

w
w

w
w

PD

PD

F-

XC

h a n g e Vi e

F-

XC

h a n g e Vi e

er

er

O W

bu

to

lic

lic

to

bu

N
.c

O W
w
.d o
c u -tr a c k

.d o

c u -tr a c k

.c

{ queue.enqueue(stack.pop()); } }

Problem 3: Symbol Table Warm-up


void CountOccurrences(ifstream & in, Map<int> & charFrequencies) { while (true) { // get the next character from the stream int nextChar = in.get(); if (nextChar == EOF) { break; } // convert it to a string for lookup in the symbol table string foundChar = ""; foundChar += char(nextChar); int frequency = 1; if (charFrequencies.containsKey(foundChar)) { frequency = charFrequencies[foundChar] + 1; } charFrequencies[foundChar] = frequency; } } char MostFrequentCharacter(ifstream & in, int & numOccurrences) { Map<int> charFrequencies; numOccurrences = 0; CountOccurrences(in, charFrequencies); Map<int>::Iterator it = charFrequencies.iterator(); string maxCharacter = ""; while (it.hasNext()) { string character = it.next(); int frequency = charFrequencies[character]; if (frequency > numOccurrences) { maxCharacter = character; numOccurrences = frequency; } } return maxCharacter[0]; }

w
w

w
w

PD

PD

F-

XC

h a n g e Vi e

F-

XC

h a n g e Vi e

er

er

O W

bu

to

lic

lic

to

bu

N
.c

O W
w
.d o
c u -tr a c k

.d o

c u -tr a c k

.c

Problem 4: Crossword Puzzle


Vector<string> AcrossWords(Grid<char> & puzzle) { Vector<string> words; for(int row = 0; row < puzzle.numRows(); row++) { bool buildingWord = false; string curWord = ""; for(int col = 0; col < puzzle.numCols(); col++) { char ch = puzzle(row, col); if(ch == '*') { //Are we building a word? If so, store and reset if(buildingWord) { if(curWord.length() >= 2) { words.add(curWord); } curWord = ""; buildingWord = false; } } else { buildingWord = true; curWord += ch; } } } return words; }

w
w

w
w

PD

PD

F-

XC

h a n g e Vi e

F-

XC

h a n g e Vi e

er

er

O W

bu

to

lic

lic

to

bu

N
.c

O W
w
.d o
c u -tr a c k

.d o

c u -tr a c k

.c

CS106B Winter 07-08

Handout #15 January 17, 2008

Section Handout #2
Problem 1: Vectors (AKA C++ ArrayLists) Say we are writing the next version of a nifty new mail reading program and want to use a Vector (interface in reader appendix) to store all the data. The following structure is used to hold the data of an email message:
struct eMailMsg { string to; string from; string message; string subject; int date; int time; }; // // // // // // i.e. i.e. body i.e. date time "professor@stanford.edu" "student@stanford.edu" of message "CS106 Rocks!" email was sent email was sent

a) How would you declare a Vector that stores eMailMsgs? b) Write a function RemoveSpam that takes a vector containing elements of type eMailMsg and removes all elements whose subject begins with the string "SPAM". c) How could you modify the to field of the eMailMsg structure so that it can hold the email addresses of an arbitrary number of recipients of an email? With the modification in place, given an eMailMsg email, how would you access the last address listed in the to field? Problem 2: Queues Write a function
void ReverseQueue(Queue<int> & q);

that reverses the elements in the passed in queue. (Hint: Is there another class that could make doing this a lot easier?) Problem 3: Using the Scanner and Stack classes
<html><b><i>CS106 rules!</i></b></html>

Web browsers use stacks to track html tags such as <b>, <i> or <html>. Every html tag must be matched by an equivalent closing tag -- </b>, </i> or </html>. Mozilla is looking for programmers to help implement this feature in the next version of Firefox and you, armed with your newly acquired knowledge of classes, decide to volunteer for the job. Using the Scanner class and the Stack class, write the following function:
bool IsCorrectlyNested(string htmlStr);

w
w

w
w

PD

PD

F-

XC

h a n g e Vi e

F-

XC

h a n g e Vi e

er

er

O W

bu

lic

lic

2
w
.c

to

to

bu

N
w

O W
.d o
c u -tr a c k

.d o

c u -tr a c k

.c

You can assume that all the tags in the html string will be correctly formed. That is, once you see an angle bracket, it will be followed by the remainder of a complete and wellformed tag (So, nothing like <<html>). Problem 4: Map Warm-up Write a function:
char MostFrequentCharacter(ifstream &if, int &numOccurrences);

that given an input file stream, returns the character that occurs the most frequently and stores the number of times it occurs in the reference parameter numOccurrences. To write this function, first start by scanning through the file stream, analyzing each character and storing an updated count in a map. Then, after youve built this table, iterate over it to find the character that occurred the most often. Problem 5: Minesweeper In the game of Minesweeper, a player searches for hidden bombs on a rectangular grid. The game board is represented by a grid of booleans marking bomb locations. A grid value is true if there is bomb at that location, false otherwise. Here is an example grid:
(0,0) T F T T F F F F T F F F F F F F T F F F T F F F F F F F F F T T T F F F

Given such a grid of bomb locations, the function MakeGridOfCounts constructs a new grid of integers storing the count of bombs in each neighborhood. The neighborhood for a location includes the location itself and its eight adjacent locations. In the returned grid, each value will be a number from 0 to 9. If passed the boolean grid above, MakeGridOfCounts returns:
(0,0) 1 3 3 3 1 0 1 3 3 4 2 1 0 2 2 3 1 1 0 1 1 2 1 1 2 4 3 2 0 0 2 3 2 1 0 0

The function is passed the grid by reference, and returns an int grid created as described.
Grid<int> MakeGridOfCounts(Grid<bool> & bombLocations)

w
w

PD

PD

F-

XC

h a n g e Vi e

F-

XC

h a n g e Vi e

er

er

O W

bu

to

lic

lic

to

bu

N
.c

O W
w
.d o
c u -tr a c k

.d o

c u -tr a c k

.c

CS106B Winter 07-08

Handout #15S January 23, 2008

Section Solutions #2
Problem 1: Vectors a) Vector<eMailMsg> mailVector; b)
void RemoveSpam(Vector<eMailMsg> & v) { for (int i = v.size() - 1; i >= 0; i--) { eMailMsg mail = v[i]; if (mail.subject.find("SPAM") == 0) { v.removeAt(i); } } }

Note that you could work forwards instead of backwards (i.e., loop from 0 to size - 1 instead of the other way around). However, if you did youd have to make sure to decrement i whenever you removed a message since otherwise youd skip an index. c) We use another Vector, of course!
struct eMailMsg { Vector<string> to; string from; string message; string subject; int date; int time; };

Access to the last element of eMailMsg email would be done by:


string lastAddress = email.to[email.to.size() 1];

Problem 2: Queues
/** * The client version of reverse queue. In order * to change the order of elements in the queue, * we use an external stack */ void ReverseQueue(Queue<int> & queue) { Stack<int> stack; while (!queue.isEmpty()) { stack.push(queue.dequeue()); } while (!stack.isEmpty())

w
w

w
w

PD

PD

F-

XC

h a n g e Vi e

F-

XC

h a n g e Vi e

er

er

O W

bu

to

lic

lic

to

bu

N
.c

O W
w
.d o
c u -tr a c k

.d o

c u -tr a c k

.c

{ queue.enqueue(stack.pop()); } }

Problem 3: Using the Scanner and Stack classes


#include "stack.h" #include "scanner.h" bool ProcessOpenTag(Scanner& scanner, Stack<string>& tagStack) { string tag = scanner.nextToken(); tagStack.push(tag); return true; } bool ProcessCloseTag(Scanner& scanner, Stack<string>& tagStack) { string tag = scanner.nextToken(); if (!tagStack.isEmpty() && tag == tagStack.pop()) { return true; }else { return false; } } bool ProcessTag(Scanner& scanner, Stack<string>& tagStack) { // read the next token to see if we found an // opening or closing tag string token = scanner.nextToken(); if (token == "/") { return ProcessCloseTag(scanner, tagStack); } else { scanner.saveToken(token); //So ProcessOpenTag can use it return ProcessOpenTag(scanner, tagStack); } } bool IsCorrectlyNested(string htmlStr) { Scanner scanner; scanner.setSpaceOption(Scanner::IgnoreSpaces); Stack<string> tagStack; scanner.setInput(htmlStr); // start by assuming it is balanced bool isBalanced = true;

w
w

w
w

PD

PD

F-

XC

h a n g e Vi e

F-

XC

h a n g e Vi e

er

er

O W

bu

to

lic

lic

to

bu

N
.c

O W
w
.d o
c u -tr a c k

.d o

c u -tr a c k

.c

while (scanner.hasMoreTokens()) { string token = scanner.nextToken(); if (token == "<") { if (!ProcessTag(scanner, tagStack)) { isBalanced = false; break; } // get rid of ">" part of tag scanner.nextToken(); } } if (!tagStack.isEmpty()) isBalanced = false; return isBalanced; }

Problem 4: Map Warm-up


char MostFrequentCharacter(ifstream &in, int &numOccurrences) { Map<int> charFrequencies; numOccurrences = 0; int nextChar; while((nextChar = in.get()) != EOF) { // convert it to a string for lookup in the symbol table string foundChar = ""; foundChar += char(nextChar); // if we find it, incremement the stored value, otherwise // enter in a new one int frequency = 1; if (charFrequencies.containsKey(foundChar)) frequency = charFrequencies[foundChar] + 1; charFrequencies[foundChar] = frequency; } // now use an iterator to find the most occurring character Map<int>::Iterator it = charFrequencies.iterator(); string maxCharacter = ""; while (it.hasNext()) { string character = it.next(); int frequency = charFrequencies[character]; if (frequency > numOccurrences)

w
w

w
w

PD

PD

F-

XC

h a n g e Vi e

F-

XC

h a n g e Vi e

er

er

O W

bu

to

lic

lic

to

bu

N
.c

O W
w
.d o
c u -tr a c k

.d o

c u -tr a c k

.c

{ maxCharacter = character; numOccurrences = frequency; } } return maxCharacter[0]; }

Problem 5: Minesweeper
bool LocationOnGrid(int row, int col, Grid<int> & bombCounts) { return row >= 0 && col >= 0 && row < bombCounts.numRows() && col < bombCounts.numCols(); } void MarkBomb(int row, int col, Grid<int> & bombCounts) { for(int bombRow = -1; bombRow <= 1; bombRow++) { for(int bombCol = -1; bombCol <= 1; bombCol++) { if(LocationOnGrid(bombRow + row, bombCol + col, bombCounts)) bombCounts(bombRow + row, bombCol + col)++; } } } Grid<int> MakeGridOfCounts(Grid<bool> & bombLocations) { Grid<int> bombCounts(bombLocations.numRows(), bombLocations.numCols()); for(int row = 0; row < bombLocations.numRows(); row++) { for(int col = 0; col < bombLocations.numCols();col++) { bombCounts(row, col) = 0; } } for(int row = 0; row < bombLocations.numRows(); row++) { for(int col = 0; col < bombLocations.numCols();col++) { if(bombLocations(row, col)) { MarkBomb(row, col, bombCounts); } } } return bombCounts; }

w
w

w
w

PD

PD

F-

XC

h a n g e Vi e

F-

XC

h a n g e Vi e

er

er

O W

bu

to

lic

lic

to

bu

N
.c

O W
w
.d o
c u -tr a c k

.d o

c u -tr a c k

.c

Note that MarkBomb uses two for loops to iterate through the 9 squares it needs to update rather than having a separate case for each square. If it had a separate case for each, this would not only be more messy and less elegant, but it would be more error prone. This is because while writing out 9 different cases, you are much more likely to make an error on one of the lines than if you are only writing out two for loops.

w
w

w
w

PD

PD

F-

XC

h a n g e Vi e

F-

XC

h a n g e Vi e

er

er

O W

bu

to

lic

lic

to

bu

N
.c

O W
w
.d o
c u -tr a c k

.d o

c u -tr a c k

.c

CS106B Winter 2009

Handout 15

Section Handout 2

January 21, 2009

Problem 1: Using the Scanner and Stack classes


<html><b><i>CS106 rules!</i></b></html>

Web browsers use stacks to track html tags such as <b>, <i> or <html>. Every html tag must be matched by an equivalent closing tag -- </b>, </i> or </html>. Microsoft is looking for programmers to help implement this feature in the next version of Internet Explorer and you, armed with your newly acquired knowledge of classes, decide to volunteer for the job. Using the Scanner and Stack classes to write the following function:
bool IsCorrectlyNested(string htmlStr);

You can assume that all the tags in the HTML string will be correctly formed. That is, once you see an angle bracket, it will be followed by the remainder of a complete and well-formed tag (So, nothing like "<<html>"). Problem 2: Queues Write a function
void ReverseQueue(Queue<int>& q);

that reverses the elements in the passed in queue. (Hint: Is there another class that could make doing this a lot easier?) Problem 3: Vectors Say we are writing the next version of Eudora and want to use a Vector to store all the data. The following structure is used to hold the data of an email message:
struct eMailMsg { string to; // i.e. "professor@stanford.edu" string from; // i.e. "student@stanford.edu" string message; // body of message string subject; // i.e. "I really like your class, but not as much as CS106" int date; // date email was sent int time; // time email was sent };

a) How would you declare a Vector that stores eMailMsgs? b) Write a function RemoveSpam that takes a vector containing elements of type eMailMsg and removes all elements whose subject begins with the string "SPAM".

w
w

w
w

PD

PD

F-

XC

h a n g e Vi e

F-

XC

h a n g e Vi e

er

er

O W

bu

2
w
w
o
.c

to

lic

lic

to

bu

N
w

O W
.d o
c u -tr a c k

.d o

c u -tr a c k

.c

c) How could you modify the to field of the eMailMsg structure so that it can hold the email addresses of an arbitrary number of recipients of an email? With the modification in place, given an eMailMsg called email, how would you access the last address listed in the to field? Problem 4: The Writing Center In an effort to improve your English Literature grade, youve decided to write a program that reads in one file (presumably your first draft) and writes the improved version to another. Feedback on prior papers suggests youd do better to cut out the adjectives and to use stronger verbs. So, youd like to automate the rewrite process. (Or rather, "youd proposition to automatize the rewrite process." Wow! That is SO MUCH CLEARER!) Write a function called ImprovePaper, which reads your first draft from the specified ifstream& and writes the "better" version to the specified ofstream&, using the supplied thesaurus (which maps words to synonym sets), and removes all adjectives and replaces all verbs with the longest synonym in the corresponding synonym set. Fortunately, someone else gave you code that magically tells you whether a word is being used as a verb or an adjective. These functions have the following prototypes:
bool isAdjective(string word); bool isVerb(string word);

You should assume that the files have been properly opened and that no error checking (beyond the end-of-file check) need be done. You should assume that the supplied thesaurus is fully populated, but you should not assume that it contains all the verbs in your paper. You should preserve spacing, and you shouldnt worry about any remnant punctuation that remains because a series of adjectives were removed. And as far as the verbs are concerned, you should assume the longer the verb, the stronger it is. If the original word happens to be longer/stronger than all of its synonyms, then retain the original word.
void ImprovePaper(ifstream& in, ofstream& out, Map<Set<string> >& thes) { out << "Heres the better paper" << endl; // write to out as you would to cout out << "-----------------------" << endl; // continue with your own code...

Problem 5: Blood Types Every persons blood has 2 markers called ABO alleles. Each of the markers is represented by one of three letters: A, B, or O. That means there are six possible combinations of these alleles that a person can have, each of them resulting in a particular ABO blood type for that person. Combination AA ABO Blood Type A

w
w

PD

PD

F-

XC

h a n g e Vi e

F-

XC

h a n g e Vi e

er

er

O W

bu

3
w
w
o
.c

to

lic

lic

to

bu

N
w

O W
.d o
c u -tr a c k

.d o

c u -tr a c k

.c

AB AO BB BO OO

AB A B B O

Likewise, every person has two alleles for the blood Rh factor, represented by the characters + and -. Someone who is "Rh positive" or "Rh+" has at least one + allele, but could have two. Someone who is "Rh negative" always has two alleles. The blood type of a person is a combination of ABO blood type and Rh factor. The blood type is written by suffixing the ABO blood type with the + or - representing the Rh factor. Examples include A+, AB-, and O-. Blood types are inherited: each biological parent donates one ABO allele (randomly chosen from their two) and one Rh factor allele to their child. Therefore 2 ABO alleles and 2 Rh factor alleles of the parents determine the childs blood type. For example, if both parents of a child have blood type A-, then the child could have either type A- or type O- blood. A child of parents with blood types A+ and B+ could have any blood type. If the ABO allele pairs, represented as strings, are mapped to their respective blood types (using a Map<string>), and blood types (also expressed as strings) are reversed-mapped back to a Vector of all possible ABO allele pairs (using a Map<Vector<string> >), then given the blood types of both mother and father, its possible to compute the spectrum of possible blood types for their children. Likewise, given the blood type of one parent and a child of that parent, its possible to infer the possible blood types of the second parent. Use the next few pages to write two functions that infer possible blood types. The first will be written to generate the set of possible blood types a child could inherit from two biological parents. The second will be written using the first to infer a second parents blood type from the first parent and the child. Go ahead and rip this page out, since you may want to read and reread it without flipping back and forth.
// utility function to generate properly ordered pairs from standalone alleles string CreateAllele(char father, char mother) { string allele; // empty C++ string to start, so that + just works if (father < mother) return allele + father + mother; return allele + mother + father; }

a. Present your implementation of the GenerateInheritableBloodTypes function that, given two blood types and the two maps described above, returns a string set containing all of the blood types a child could inherit from his or her parents. You should assume that the two maps are populated with data for the six associations

w
w

PD

PD

F-

XC

h a n g e Vi e

F-

XC

h a n g e Vi e

er

er

O W

bu

4
w
w
o
.c

to

lic

lic

to

bu

N
w

O W
.d o
c u -tr a c k

.d o

c u -tr a c k

.c

above, and you may use the CreateAllele utility function to make sure the two alleles are properly ordered in the allele pair (so you generate "AB" instead of "BA").

/** * Function: GenerateInheritableBloodTypes * Usage: types = GenerateInheritableBloodTypes("A-", "O+", alleles, bloodTypes); * --------------------------------------* Given the two very small maps, generate the set of blood types that * a mother and father's biological child could inherit, given the mother's * and father's blood types. You may assume that all of the data is legitimate * so that you needn't do any error checking whatsoever. The two maps * only include allele and ABO blood types, but nothing about Rh factors. * So, alleleMap will include pairs like ("BO","B"), and bloodTypeMap * will contain pairs like ("B", ["BB, BO"]) and ("O", ["OO"]). * * The set thats returned will include Rh factors with the blood types-* strings like "AB+", "AB-", and "O-" might be included. */ Set<string> GenerateInheritableBloodTypes(string mother, string father, Map<string>& alleleMap, Map<Vector<string> >& bloodTypeMap) {

b. Now write the InferPossibleParentBloodTypes function, which computes and returns the set of possible blood types that could have combined with the known parent blood type to conceive a child with the child blood type. Do so by iterating over the key set of the bloodTypeMap, pairing each key up with a "+" or a "-", and tracking whether each blood type, when matched against the other parent blood type, could combine to produce the child blood type. Youll want to use your GenerateInheritableBloodTypes function from part a.
/** * Function: InferPossibleParentBloodTypes * Usage: possibilities = * InferPossibleParentBloodTypes ("O-", "A+", alleles, bloodTypes); * ------------------------------------------------* Information about a father's blood type and his child's blood type * places constraints on what the mother's blood type might be. * InferPossibleParentBloodTypes determines what the mother's blood * type might be by computing and returning the set of all possibilities. * This is done by iterating over all the keys of the bloodType map * to generate all blood-type/rh-factor pairs, and including those that * in principle could combine with the identified parent's blood type to * generate the child blood type. */ Set<string> InferPossibleParentBloodTypes(string parent, string child, Map<string>& alleleMap, Map<Vector<string> >& bloodTypeMap) {

w
w

PD

PD

F-

XC

h a n g e Vi e

F-

XC

h a n g e Vi e

er

er

O W

bu

to

lic

lic

to

bu

N
.c

O W
w
.d o
c u -tr a c k

.d o

c u -tr a c k

.c

CS106B Winter 2009

Handout 15S

Section Solution 2

January 21 22, 2009

Problem 1: Using the Scanner and Stack classes


#include "stack.h" #include "scanner.h" bool ProcessOpenTag(Scanner& scanner, Stack<string>& tagStack) { string tag = scanner.nextToken(); tagStack.push(tag); return true; } bool ProcessCloseTag(Scanner& scanner, Stack<string>& tagStack) { string tag = scanner.nextToken(); return !tagStack.isEmpty() && tag == tagStack.pop()); } bool ProcessTag(Scanner& scanner, Stack<string>& tagStack) { // read the next token to see if we found an // opening or closing tag string token = scanner.nextToken(); if (token == "/") return ProcessCloseTag(scanner, tagStack); scanner.saveToken(token); // So ProcessOpenTag can use it return ProcessOpenTag(scanner, tagStack); } bool IsCorrectlyNested(string htmlStr) { Scanner scanner; scanner.setSpaceOption(Scanner::IgnoreSpaces); scanner.setInput(htmlStr); Stack<string> tagStack; while (scanner.hasMoreTokens()) { string token = scanner.nextToken(); if (token == "<") { if (!ProcessTag(scanner, tagStack)) return false; scanner.nextToken(); // skip over ">" } } return tagStack.isEmpty(); }

w
w

w
w

PD

PD

F-

XC

h a n g e Vi e

F-

XC

h a n g e Vi e

er

er

O W

bu

2
w
w
o
.c

to

lic

lic

to

bu

N
w

O W
.d o
c u -tr a c k

.d o

c u -tr a c k

.c

Problem 2: Queues
#include "queue.h" #include "stack.h" /** * Function: ReverseQueue * ---------------------* The client version that reverses a Queue. In order to change * the order of elements in the queue, we secretly use a Stack to * order the elements in reverse. (We could have used a Vector as * as well.) */ void ReverseQueue(Queue<int>& queue) { Stack<int> stack; while (!queue.isEmpty()) stack.push(queue.dequeue()); while (!stack.isEmpty()) queue.enqueue(stack.pop()); }

Problem 3: Vectors Say we are writing the next version of Eudora and want to use a Vector to store all the data. The following structure is used to hold the data of an email message:
struct eMailMsg { string to; // i.e. "professor@stanford.edu" string from; // i.e. "student@stanford.edu" string message; // body of message string subject; // i.e. "I really like your class, but not as much asCS106" int date; // date email was sent int time; // time email was sent };

a) How would you declare a Vector that stores eMailMsgs?


Vector<eMailMsg> mailVector;

b) Write a function RemoveSpam that takes a vector containing elements of type eMailMsg and removes all elements whose subject begins with the string "SPAM".
void RemoveSpam(Vector<eMailMsg>& inbox) { for (int i = inbox.size() - 1; i >= 0; i--) { if (index[i].subject.find("SPAM") == 0) inbox.removeAt(i); } }

c) How could you modify the to field of the eMailMsg structure so that it can hold the email addresses of an arbitrary number of recipients of an email? With the modification in place, given an eMailMsg called email, how would you access the last address listed in the to field?

w
w

PD

PD

F-

XC

h a n g e Vi e

F-

XC

h a n g e Vi e

er

er

O W

bu

3
w
w
o
.c

to

lic

lic

to

bu

N
w
.d o
c u -tr a c k

O W

.d o

c u -tr a c k

.c

We use another Vector, of course!


struct eMailMsg { Vector<string> to; string from; string message; string subject; int date; int time; };

Access to the last element of eMailMsg called email would be done by:
string lastAddress = email.to[email.to.size() 1];

Problem 4: The Writing Center


void ImprovePaper(ifstream& in, ofstream& out, Map<Set<string> >& thes) { while (true) { string line; getline(in, line); if (in.eof()) break; ProcessLine(line, out, thes); } } void ProcessLine(string line, ofstream& out, Map<Set<string> >& thes) { Scanner scanner; scanner.setInputLine(line); while (scanner.hasMoreTokens()) { string token = scanner.nextToken(); if (!isAdjective(token)) { if (isVerb(token)) { token = FindMostImpressiveWord(token, thes); } out << token; } } out << endl; } string FindMostImpressiveWord(string verb, Map<Set<string> >& thes) { if (!thes.containsKey(verb)) return verb; Set<string>& synonyms = thes[verb]; string strongestVerb = verb; Set<string>::Iterator iter = synonyms.iterator(); while (iter.hasNext()) { string synonym = iter.next(); if (synonym.length() > strongestVerb.length()) { strongestVerb = synonym;

w
w

PD

PD

F-

XC

h a n g e Vi e

F-

XC

h a n g e Vi e

er

er

O W

bu

4
w
w
o
.c

to

lic

lic

to

bu

N
w

O W
.d o
c u -tr a c k

.d o

c u -tr a c k

.c

} } return strongestVerb; }

Problem 5: Blood Types a.


/** * Function: GenerateInheritableBloodTypes * Usage: types = GenerateInheritableBloodTypes("A-", "O+", alleles, bloodTypes); * --------------------------------------* Given the two very small maps, generate the set of blood types that * a mother and father's biological child could inherit, given the mother's * and father's blood types. You may assume that all of the data is legitimate * so that you needn't do any error checking whatsoever. The two maps * only include allele and ABO blood types, but nothing about Rh factors. * So, alleleMap will include pairs like ("BO","B"), and bloodTypeMap * will contain pairs like ("B", ["BB, BO"]) and ("O", ["OO"]). * * The set thats returned will include Rh factors with the blood types-* strings like "AB+", "AB-", and "O-" might be included. */ Set<string> GenerateInheritableBloodTypes(string mother, string father, Map<string>& alleleMap, Map<Vector<string> >& bloodTypeMap) { Set<string> possibilities; Vector<string>& fatherAlleles = bloodTypeMap[father.substr(0, father.size()-1)]; Vector<string>& motherAlleles = bloodTypeMap[mother.substr(0, mother.size()-1)]; bool onlyNegativeRhPossible = (father[father.size() - 1] == '-' && mother[mother.size() - 1] == '-'); for (int f = 0; f < fatherAlleles.size(); f++) { for (int m = 0; m < motherAlleles.size(); m++) { string fatherAllele = fatherAlleles[f]; string motherAllele = motherAlleles[m]; for (int i = 0; i < fatherAllele.size(); i++) { for (int j = 0; j < motherAllele.size(); j++) { string inheritedAllele = CreateAllele(fatherAllele[i], motherAllele[j]); string inheritedBloodType = alleleMap[inheritedAllele]; possibilities.add(inheritedBloodType + '-'); if (!onlyNegativeRhPossible) possibilities.add(inheritedBloodType + '+'); } } } } return possibilities; }

w
w

PD

PD

F-

XC

h a n g e Vi e

F-

XC

h a n g e Vi e

er

er

O W

bu

5
w
w
o
.c

to

lic

lic

to

bu

N
w

O W
.d o
c u -tr a c k

.d o

c u -tr a c k

.c

b.

/** * Function: InferPossibleParentBloodTypes * Usage: possibilities = * InferPossibleParentBloodTypes ("O-", "A+", alleles, bloodTypes); * ------------------------------------------------* Information about a father's blood type and his child's blood type * places constraints on what the mother's blood type might be. * InferPossibleParentBloodTypes determines what the mother's blood * type might be by computing and returning the set of all possibilities. * This is done by iterating over all the keys of the bloodType map * to generate all blood-type/rh-factor pairs, and including those that * in principle could combine with the identified parent's blood type to * generate the child blood type. */ Set<string> InferPossibleParentBloodTypes(string parent, string child, Map<string>& alleleMap, Map<Vector<string> >& bloodTypeMap) { Set<string> possibilities; Map<Vector<string> >::Iterator iter = bloodTypeMap.iterator(); while (iter.hasNext()) { string bloodType = iter.next(); Set<string> babyBloodTypes; babyBloodTypes = GenerateInheritableBloodTypes(parent, bloodType + '-', alleleMap, bloodTypeMap); if (babyBloodTypes.contains(child)) possibilities.insert(bloodType + '-'); babyBloodTypes = GenerateInheritableBloodTypes(parent, bloodType + '+', alleleMap, bloodTypeMap); if (babyBloodTypes.contains(child)) possibilities.insert(bloodType + '+'); } return possibilities; }

w
w

PD

PD

F-

XC

h a n g e Vi e

F-

XC

h a n g e Vi e

er

er

O W

bu

to

lic

lic

to

bu

N
.c

O W
w
.d o
c u -tr a c k

.d o

c u -tr a c k

.c

CS106B Winter 2010

Handout 12

Section Handout

January 18th, 2010

Problem 1: String Explosions Given a string str and a character delim, write a function called Explode which returns the "explosion" of str in a Vector<string>, which is the ordered collection of strs delim-delimited substrings. Here are some examples of how Explode should work: Explode("171.64.42.111", '.'); should return ["171", "64", "42", "111"] Explode("usr/class/cs106x/WWW", '/'); should return ["usr", "class", cs106x", "WWW"] Explode("XOXXOOOXXOOX", 'X) should return ["", "O", "", "OOO", "", "OO", ""] Note the last example makes it clear what happens when the delim characters appear on either end of the str, or when two instances of delim appear consecutively. In general, a string str with k instances of delim returns a Vector<string> of length k + 1.
Vector<string> Explode(string str, char delim);

Problem 2: URL Parameter Map A URL has many components. a protocol, a domain, and a file path are almost always included. Sometimes the URL includes a query, which the portion of the URL that appears after the ? and (if present) before the # (which defines the URLs hash). Here are some examples: http://www.google.com/ig?hl=en&source=iglk http://www.facebook.com/profile.php?id=1160&viewas=214707 http://store.apple.com/us/browse/family/iphone?mco=MTAyNTM5ODU

And while you may recognize the ? as a common character within URLs, you may not realize that the part following the ? is the serialization of a Map<string>. The query string is an &delimited string of key-value pairs, where each key-value pair takes the form <key>=<value>. When a web server gets an HTTP request, it digests the query string and re-hydrates it into Map<string> form, and uses that map to programmatically shape how the response page is generated. Thats why www.google.com/ig?hl=en&source=iglk generates English, and www.google.com/ig?hl=fr&source=iglk generates French. One little wrinkle: the value is technically optional, so that query strings like type=5&seeall and source_id=9074&read= are legitimate. When a key is present

w
w

w
w

PD

PD

F-

XC

h a n g e Vi e

F-

XC

h a n g e Vi e

er

er

O W

bu

2
w
w
o
.c

to

lic

lic

to

bu

N
w

O W
.d o
c u -tr a c k

.d o

c u -tr a c k

.c

without a value, then the value is arbitrary and the presence of the key is the only thing of interest. In such cases, the Map<string> should include the key and attach an arbitrary value to it. Write a function that takes a legitimate URL, extracts the query string, and returns a Map<string> with all of its key-value pairs. You can safely assume that the URL is properly formatted, and that the structure of an URL is no more complicated than whats described here. Make sure you deal with URLs that dont include a query string, and URLs that include a hash.
Map<string> extractQueryMap(string url);

This problem is as much about string manipulation as it is about the Map, so dont be surprised if youre using some advanced string methods that havent come up in any previous examples. Problem 3: Publishing Stories Social networking sites like Facebook, LinkedIn, Orkut, Friendster, and MySpace typically record and publish stories about actions taken by you and your friends. Stories such as: John Dixon accepted your friend request. Jeff Barbose is no longer in a relationship. Scott James wrote a note called "The Two Percent Solution". Arlene Heitner commented on Melodie Bowshers video. Antonio Melara gave The French Laundry a 5-star review. are created from story templates like {name} accepted your friend request. {name} is no longer in a relationship. {name} wrote a note called "{title}". {name} commented on {target}s video. {actor} gave {restaurant} a {rating}-star review. The specific story is generated from the skeletal one by replacing the tokenssubstrings like "{name}", "{title}", and "{rating}"with event-specific values, like "John Dixon", "The Two Percent Solution", and "5". The token-value pairs can be packaged in a Map<string>, and given a story template and a data map, its possible to generate an actual story. Write the SubstituteTokens function, which accepts a story template (like "{actor} gave {restaurant} a {rating}-star review.") and a Map<string> (which might map "actor" to "Antonio Melara", "restaurant" to

w
w

PD

PD

F-

XC

h a n g e Vi e

F-

XC

h a n g e Vi e

er

er

O W

bu

3
w
w
o
.c

to

lic

lic

to

bu

N
w

O W
.d o
c u -tr a c k

.d o

c u -tr a c k

.c

"The French Laundry", and "rating" to "5"), and builds a string just like the story template, except that the tokens have been replaced by the text they map to. Assume the following is true: '{' and '}' exist to delimit token names, but wont appear anywhere else. In other words, if you encounter the '{' character, you can assume it marks the beginning of a token that ends with a '}'. We guarantee that all tokens are in the Map<string>. You dont need to do any error checking. Note that the '{' and the '}' arent included in the map. "{name}", for instance, is replaced with whatever "name" identifies in the map. The prototype is:
string SubstituteTokens(string storyTemplate, Map<string>& data);

Problem 4: Happy Numbers Starting with any positive integer n, replace n with the sum of the squares of its digits, and repeat the process until the number equals 1, or until you arrive at a previously arrived at number (and have thus entered a cycle). Numbers eventually leading to 1 are called happy numbers, and the rest are called unhappy, otherwise know as sad, numbers. Using a Set<int>, write the IsHappy predicate function, which accepts a positive number and returns true if and only if the number is happy, and false otherwise.
bool IsHappy(int n);

Problem 5: FilterPossibilities Implement the FilterPossibilities function, which takes a reference to a Set<string> called words and a reference to a Map<int> called letters, and removes strings from words unless they meet the requirements imposed by the letters map. The letters map maps characters (stored as one-character strings) to the maximum number of times that letter may appear in a word if its to be retained by the words set. (If the character doesnt appear as a key in the map, then there are no restrictions on that character.) For instance, if the words set contains "abacus", "gizmo", "holler", "llama", and "portions", and the letters map maps "a" to 1, "l" to 1, and "p" to 3, then FilterPossibilities would remove "abacus" (too many as), "holler" (too many ls), and "llama" (too many of both!), leaving just "gizmo" and "portions".
void FilterPossibilities(Set<string>& words, Map<int>& letters);

w
w

PD

PD

F-

XC

h a n g e Vi e

F-

XC

h a n g e Vi e

er

er

O W

bu

to

lic

lic

to

bu

N
.c

O W
w
.d o
c u -tr a c k

.d o

c u -tr a c k

.c

CS106B Winter 2010

Handout 12S

Section Solution

January 20th 22nd, 2009

Solution 1: String Explosions


Vector<string> Explode(string str, char delim) { Vector<string> explosion; string cluster; str += delim; // last cluster gets appended w/ minimal special casing for (int i = 0; i < str.size(); i++) { if (str[i] == delim) { explosion.add(cluster); cluster.clear(); // cluster = "" works, too } else { cluster += str[i]; } } return explosion; }

Solution 2: URL Parameter Map


Map<string> extractQueryMap(string url) { Map<string> parameters; int index = url.find('?'); if (index == string::npos) return parameters; string query = url.substr(index + 1); index = query.find('#'); if (index != string::npos) { query = query.substr(0, index); // chop off hash } Vector<string> pairs = Explode(query, '&'); for (int i = 0; i < pairs.size(); i++) { Vector<string> components = Explode(pairs[i], '='); string key = components[0]; string value = "true"; if (components.size() > 1 && !components[1].empty()) { value = components[1]; } parameters[key] = value; } return parameters; }

w
w

w
w

PD

PD

F-

XC

h a n g e Vi e

F-

XC

h a n g e Vi e

er

er

O W

bu

2
w
w
o
.c

to

lic

lic

to

bu

N
w

O W
.d o
c u -tr a c k

.d o

c u -tr a c k

.c

Solution 3: Publishing Stories Theres the one-pass approach that just appends characters from the template to the running story, unless that character is an '{', in which case we extract everything up through the matching '}' and substitute it with a string from the data map.
string SubstituteTokens(string storyTemplate, Map<string>& data) { string story; for (int i = 0; i < storyTemplate.size(); i++) { if (storyTemplate[i] != '{') { story += storyTemplate[i]; } else { int j = storyTemplate.find('}', i + 1); string token = storyTemplate.substr(i + 1, j - i - 1); story += data.get(token); i = j; } } return story; }

Another approach is to iterate over the data map and drive the substitution that way. Its less efficient, but its another way to think about the problem and is a perfectly acceptable answer for the purposes of a discussion section.
string SubstituteOneToken(string story, string token, string value) { int start = 0; while (true) { int found = story.find(token); if (found == string::npos) return story; story.replace(found, token.size(), value); start = found + value.size() + 1; } } string SubstituteTokens(string storyTemplate, Map<string>& data) { string story = storyTemplate; foreach (string token in data) { story = SubstituteOneToken(story, '{' + token + '}', data.get(token)); } return story; }

w
w

PD

PD

F-

XC

h a n g e Vi e

F-

XC

h a n g e Vi e

er

er

O W

bu

3
w
w
o
.c

to

lic

lic

to

bu

N
w

O W
.d o
c u -tr a c k

.d o

c u -tr a c k

.c

Solution 4: Happy Numbers


int ComputeNext(int num) { int sumOfSquares = 0; while (num > 0) { int digit = num % 10; sumOfSquares += digit * digit; num /= 10; } return sumOfSquares; } bool IsHappy(int num) { Set<int> previouslySeen; while (num > 1 && !previouslySeen.contains(num)) { previouslySeen.add(num); num = ComputeNext(num); } return num == 1; }

Solution 5: FilterPossibilities
int CountLetters(string str, char letter) { int count = 0; for (int i = 0; i < str.size(); i++) { if (str[i] == letter) { count++; } } return count; } void FilterPossibilities(Set<string>& words, Map<int>& lettersMap) { Set<string> wordsToDelete; foreach (string word in words) { foreach (string key in letterMap) { char letter = key[0]; if (CountLetters(word, letter) > lettersMap[key]) { wordsToDelete.add(word); break; // words already been marked for removal... move on } } Another solution might build up a letter frequency } words.subtract(wordsToDelete); }

map of all of the words in words, and that make sure that all counts in this frequency map are less than the corresponding counts in lettersMap.

w
w

PD

PD

F-

XC

h a n g e Vi e

F-

XC

h a n g e Vi e

er

er

O W

bu

to

lic

lic

to

bu

N
.c

O W
w
.d o
c u -tr a c k

.d o

c u -tr a c k

.c

Eric Roberts CS106B

Handout #18 January 19, 2011

Section Handout #2ADTs


Parts of this handout were written by Julie Zelenski.

Problem 1. Using grids In the game of Minesweeper, a player searches for hidden bombs on a rectangular grid. One way to represent that grid in C++ is to use a grid of Boolean values marking bomb locations. A grid value is true if there is bomb at that location, false otherwise. Here is an example grid:
(0,0) T F T T F F F F T F F F F F F F T F F F T F F F F F F F F F T T T F F F

Given such a grid of bomb locations, write a function


void MakeGridOfCounts(Grid<bool> & locations, Grid<int> & counts);

that constructs a grid of integers storing the count of bombs in each neighborhood. The neighborhood of a location includes the location itself and the eight adjacent locations, but only if they are inside the boundaries of the grid. The reference parameter counts is used to store the result; your job is to make sure that it has the same size as the locations grid and then to assign to each element an integer between 0 and 9. For example, if sampleBombLocations contains the Boolean grid shown earlier, the code
Grid<int> sampleBombCounts; MakeGridOfCounts(sampleBombLocations, sampleBombCounts);

should initialize sampleBombCounts as follows:


(0,0) 1 3 3 3 1 0 1 3 3 4 2 1 0 2 2 3 1 1 0 1 1 2 1 1 2 4 3 2 0 0 2 3 2 1 0 0

Problem 2. Using queues (Chapter 4, exercise 9, page 169) Bob Dylans 1963 song The Times They Are A-Changin contains the following lines, which are themselves paraphrased from Matthew 19:30:

w
w

w
w

PD

PD

F-

XC

h a n g e Vi e

F-

XC

h a n g e Vi e

er

er

O W

bu

lic

lic

2
w
.c

to

to

bu

N
w

O W
.d o
c u -tr a c k

.d o

c u -tr a c k

.c

And the first one now Will later be last For the times they are a-changin In keeping with this revolutionary sentiment, write a function
void ReverseQueue(Queue<string> & queue);

that reverses the elements in the queue. Remember that you have no access to the internal representation of the queue and will need to come up with an algorithm, presumably involving other data structures, that accomplishes the task. Problem 3. Using maps (Chapter 4, exercise 14, page 170) In May of 1844, Samuel F. B. Morse sent the message What hath God wrought! by telegraph from Washington to Baltimore, heralding the beginning of the age of electronic communication. To make it possible to communicate information using only the presence or absence of a single tone, Morse designed a coding system in which letters and other symbols are represented as coded sequences of short and long tones, traditionally called dots and dashes. In Morse code, the 26 letters of the alphabet are represented by the following codes: A B C D E F G H I J K L M N O P Q R S T U V W X Y Z

If you want to convert from letters to Morse code, you could use a switch statement or store the strings for each letter in a vector with 26 elements; to convert from Morse code to letters, the easiest approach is to use a map. Write a program that reads in lines from the user and translates each line either to or from Morse code depending on the first character of the line: If the line starts with a letter, you want to translate it to Morse code. Any characters other than the 26 letters should simply be ignored. If the line starts with a period (dot) or a hyphen (dash), it should be read as a series of Morse code characters that you need to translate back to letters. Each sequence of dots and dashes is separated by spaces, but any other characters should be ignored.

The program should end when the user enters a blank line. A sample run of this program (taken from the messages between the Titanic and the Carpathia in 1912) might look like this (note that there are no spaces in the Morse-to-letters translation):

w
w

PD

PD

F-

XC

h a n g e Vi e

F-

XC

h a n g e Vi e

er

er

O W

bu

lic

lic

3
w
.c

to

to

bu

N
w

O W
.d o
c u -tr a c k

.d o

c u -tr a c k

.c

Note: In this course, we work very hard to cure you of the bad habit of using global variables, which turn out to be common sources of hard-to-find bugs in programs. We do, however, allow the use of global constants, mostly because the otherwise vexing question of who is manipulating those values goes away. This exercise represents something of an intermediate position. The map that translates between English and Morse Code characters and the inverted map that translates in the opposite direction never change once they are initialized and therefore function effectively as constants, even if C++ doesnt make it easy to mark them as such. Given this fact, it seems appropriate to define these maps as global variables instead of defining them in the main program and then passing them as parameters. Problem 4. Using lexicons (Chapter 4, exercise 15, page 171) In Chapter 3, exercise 6, you were asked to write a function IsPalindrome that checks whether a word is a palindrome, which means that it reads identically forward and backward. Use that function together with the lexicon of English words to print out a list of all words in English that are palindromes. Problem 5. Processing maps in alphabetical order (Chapter 4, exercise 16, page 171) In Chapter 4, we introduced the program wordfreq.cpp (see Figure 4-8) that outputted the number of times each word in a given data file appears in that file. As noted in the chapter, it is actually rather easy to change the wordfreq.cpp program so that the words appear in alphabetical order. The only thing you need to do is think creatively about the tools that you already have. Rewrite the function
void DisplayWordCounts(Map<int> & wordCounts);

so that it outputs a given map of words to word counts such that words are listed alphabetically.

w
w

PD

PD

F-

XC

h a n g e Vi e

F-

XC

h a n g e Vi e

er

er

O W

bu

to

lic

lic

to

bu

N
.c

O W
w
.d o
c u -tr a c k

.d o

c u -tr a c k

.c

Eric Roberts CS106B

Handout #18A January 19, 2011

Solutions to Section Handout #2


Problem 1. Using grids
/* * Function: MakeGridOfCounts * Usage: MakeGridOfCounts(locations, counts); * ------------------------------------------* This function uses the first grid to indicate where mines are * located and creates a second grid showing the count of mines * in the neighborhood of each square. */ void MakeGridOfCounts(Grid<bool> & locations, Grid<int> & counts) { int nRows = locations.numRows(); int nCols = locations.numCols(); counts.resize(nRows, nCols); for (int i = 0; i < nRows; i++) { for (int j = 0; j < nCols; j++) { counts[i][j] = CountBombNeighbors(locations, i, j); } } } /* * Function: CountBombNeighbors * Usage: int nBombs = CountBombNeighbors(locations, row, col); * -----------------------------------------------------------* Counts the number of bombs in the immediate neighborhood. */ int CountBombNeighbors(Grid<bool> & locations, int row, int col) { int nBombs = 0; for (int i = -1; i <= 1; i++) { for (int j = -1; j <= 1; j++) { if (locations.inBounds(row + i, col + j)) { if (locations[row + i][col + j]) nBombs++; } } } return nBombs; }

w
w

w
w

PD

PD

F-

XC

h a n g e Vi e

F-

XC

h a n g e Vi e

er

er

O W

bu

lic

lic

2
w
.c

to

to

bu

N
w

O W
.d o
c u -tr a c k

.d o

c u -tr a c k

.c

Problem 2. Using queues


/* * Function: ReverseQueue * Usage: ReverseQueue(queue); * --------------------------* Reverses the order of the elements in a queue. This * implementation does so by using a stack to hold the * values as they are being reversed. */ void ReverseQueue(Queue<string> & queue) { Stack<string> stack; while (!queue.isEmpty()) { stack.push(queue.dequeue()); } while (!stack.isEmpty()) { queue.enqueue(stack.pop()); } }

Problem 3. Using maps


/* * File: MorseCode.cpp * -----------------* This program translates to and from Morse Code using maps to * assist in the translation. */ #include #include #include #include #include "genlib.h" "simpio.h" "map.h" "strutils.h" <iostream>

/* Function protoypes */ string TranslateLettersToMorse(string line); string TranslateMorseToLetters(string line); Map<string> CreateMorseCodeMap(); Map<string> InvertMap(Map<string> & map); /* * Constant maps: LETTERS_TO_MORSE, MORSE_TO_LETTERS * ------------------------------------------------* These variables contain maps that convert in each direction * between uppercase letters and their Morse Code equivalent. * Because these variables are initialized once and retain their * values throughout the lifetime of the program, they are best * treated as constants that are shared among the various functions * instead of as variables that are passed as parameters. */ Map<string> LETTERS_TO_MORSE = CreateMorseCodeMap(); Map<string> MORSE_TO_LETTERS = InvertMap(LETTERS_TO_MORSE);

w
w

PD

PD

F-

XC

h a n g e Vi e

F-

XC

h a n g e Vi e

er

er

O W

bu

lic

lic

3
w
.c

to

to

bu

N
w

O W
.d o
c u -tr a c k

.d o

c u -tr a c k

.c

/* Main program */ int main() { cout << "Morse code translator" << endl; while (true) { cout << "> "; string line = ConvertToUpperCase(GetLine()); if (line == "") break; if (line[0] == '.' || line[0] == '-') { cout << TranslateMorseToLetters(line) << endl; } else { cout << TranslateLettersToMorse(line) << endl; } } return 0; } /* * Function: TranslateLettersToMorse * Usage: string morse = TranslateLettersToMorse(line); * ---------------------------------------------------* Translates a string of letters into Morse Code characters * separated by spaces. Characters that don't appear in the table * are simply ignored. */ string TranslateLettersToMorse(string line) { string morse = ""; for (int i = 0; i < line.length(); i++) { string letter = ConvertToUpperCase(line.substr(i, 1)); if (LETTERS_TO_MORSE.containsKey(letter)) { if (morse != "") morse += " "; morse += LETTERS_TO_MORSE[letter]; } } return morse; } /* * Function: TranslateMorseToLetters * Usage: string letters = TranslateLettersToMorse(line); * -----------------------------------------------------* Translates a string in Morse Code into English letters. * Because word breaks are not represented in Morse code, the * letters in the output will be run together. The characters * of the Morse Code input must be separated by a single space. * Any other character in the input is simply ignored. If there * is no English equivalent for the Morse Code character, this * function indicates that fact by inserting a question mark (?). * * Implementation note: To eliminate the special case of the last * character in the line, this function begins by adding a space * to the end of the input string. */

w
w

PD

PD

F-

XC

h a n g e Vi e

F-

XC

h a n g e Vi e

er

er

O W

bu

lic

lic

4
w
.c

to

to

bu

N
w

O W
.d o
c u -tr a c k

.d o

c u -tr a c k

.c

string TranslateMorseToLetters(string line) { line += " "; string letters = ""; string morse = ""; for (int i = 0; i < line.length(); i++) { char ch = line[i]; if (ch == '.' || ch == '-') { morse += ch; } else if (ch == ' ') { if (MORSE_TO_LETTERS.containsKey(morse)) { letters += MORSE_TO_LETTERS[morse]; } else { letters += '?'; } morse = ""; } } return letters; } /* * Function: CreateMorseCodeMap * Usage: Map<string> map = CreateMorseCodeMap(); * ---------------------------------------------* Returns a map in which each uppercase letter is mapped into its * Morse code equivalent. */ Map<string> CreateMorseCodeMap() { Map<string> map; map["A"] = ".-"; map["B"] = "-..."; map["C"] = "-.-."; map["D"] = "-.."; map["E"] = "."; map["F"] = "..-."; map["G"] = "--."; map["H"] = "...."; map["I"] = ".."; map["J"] = ".---"; map["K"] = "-.-"; map["L"] = ".-.."; map["M"] = "--"; map["N"] = "-."; map["O"] = "---"; map["P"] = ".--."; map["Q"] = "--.-"; map["R"] = ".-."; map["S"] = "..."; map["T"] = "-"; map["U"] = "..-"; map["V"] = "...-"; map["W"] = ".--"; map["X"] = "-..-"; map["Y"] = "-.--"; map["Z"] = "--.."; return map; }

w
w

PD

PD

F-

XC

h a n g e Vi e

F-

XC

h a n g e Vi e

er

er

O W

bu

lic

lic

5
w
.c

to

to

bu

N
w

O W
.d o
c u -tr a c k

.d o

c u -tr a c k

.c

/* * Function: InvertMap * Usage: Map<string> inverse = InvertMap(map); * -------------------------------------------* Creates an inverted copy of the specified map in which the values * in the original become the keys of the new map and refer back to * their associated keys. Thus, if "abc" is bound to "xyz" in the * original map, the inverted map will bind "xyz" to "abc". If two * keys in the original map have the same value, this function will * signal an error condition. */ Map<string> InvertMap(Map<string> & map) { Map<string> inverse; foreach (string key in map) { string value = map[key]; if (inverse.containsKey(value)) { Error("That map cannot be inverted"); } inverse[value] = key; } return inverse; }

Problem 4. Using lexicons


/* * This program lists all palindromes in the English dictionary. */ int main() { cout << "This program finds all palindromes." << endl; Lexicon english("EnglishWords.dat"); foreach (string word in english) { if (word == ReverseString(word)) { cout << word << endl; } } return 0; } /* * Function: ReverseString * Usage: string rev = ReverseString(str); * --------------------------------------* Returns a copy of str with the letters reversed. */ string ReverseString(string str) { string rev = ""; for (int i = str.length() - 1; i >= 0; i--) { rev += str[i]; } return rev; }

w
w

PD

PD

F-

XC

h a n g e Vi e

F-

XC

h a n g e Vi e

er

er

O W

bu

lic

lic

6
w
.c

to

to

bu

N
w

O W
.d o
c u -tr a c k

.d o

c u -tr a c k

.c

Problem 5. Processing maps in alphabetical order


/* * Displays the count associated with each word in the frequency * table. This version uses a lexicon to ensure that the words * appear in alphabetical order. */ void DisplayWordCounts(Map<int> & wordCounts) { Lexicon words; foreach (string word in wordCounts) { words.add(word); } foreach (string word in words) { cout << left << setw(15) << word << right << setw(5) << wordCounts[word] << endl; } }

w
w

Das könnte Ihnen auch gefallen