part I due by 10:00 p.m. on Friday, July 11, 2025
part II due by 10:00 p.m. on Monday, July 14, 2025
In your work on this assignment, make sure to abide by the policies on academic conduct for this course.
If you have questions while working on this assignment, please come to
office hours, post them on Ed Discussion, or email
cscis111-staff@lists.fas.harvard.edu
50 points total
If you haven’t already created a folder named s111
for your
work in this course, follow these
instructions to do so.
Then create a subfolder called ps4
within your s111
folder,
and put all of the files for this assignment in that folder.
The problems from Part I will all be completed in a single PDF file. To create it, you should do the following:
Access the template that we have created by clicking on this link and signing into your Google account as needed.
When asked, click on the Make a copy button, which will save a copy of the template file to your Google Drive.
Select File->Rename, and change the name of the file to
ps4_partI
.
Add your work for the problems from Part I to this file.
Once you have completed all of these problems, choose
File->Download->PDF document, and save the PDF file in your
ps4
folder. The resulting PDF file (ps4_partI.pdf
) is the one that
you will submit. See the submission guidelines at the end of Part I.
8 points total; individual-only
(2 points) What is the output of the following lines of code?
int[] y = {6, 8, 10, 12, 14, 16}; System.out.println(y[4] + " " + y.length); y[0] = 2 * y[1]; System.out.println(Arrays.toString(y));
(Note: if you want to create a test class to check your answer,
you’ll need to import the java.util
package so that you
can use the Arrays.toString
method.)
(3 points) What is the output of the following lines of code?
int[] foo = {1, 2, 3, 4}; int[] bar = foo; int[] baz = new int[foo.length]; for (int i = 0; i < foo.length; i++) { baz[i] = foo[i]; } foo[2] = 5; System.out.println(foo[2] + " " + bar[2] + " " + baz[2]);
After giving the output, explain briefly why it makes sense.
(3 points) Consider the following method:
public static void mystery(int[] values) { int val = values[0]; for (int i = 0; i < values.length - 1; i++) { values[i] = values[i + 1]; values[i]++; } values[values.length - 1] = val + 1; }
Explain briefly what this method does. To figure it out, you may find it helpful to trace through the execution of one or more method calls like the following, each of which operates on one of the arrays from the problems above:
int[] y = {6, 8, 10, 12, 14, 16}; mystery(y);
Although you should ultimately be able to trace through code like this on paper, one good supplemental tool for tracing is Java Tutor – which will also strengthen your understanding of what happens in memory!
For this problem, after clicking on the above link, you would then take the following steps to trace through the code in Java Tutor:
Scroll down to the provided template for a class called
MainClass
– skipping over the example Person
class
that is found at the top of the page.
Copy the mystery
method shown above into MainClass
,
above the provided main
method.
Add test calls like the one shown above to the provided
main
method.
Click the Visualize Execution button, and wait for Java Tutor to prepare the visualization.
Repeatedly click the Next button to see what happens as the code executes, one step at a time. Make sure to review the pictures of memory that are provided on the right side of the visualization.
For future reference, there is a link to Java Tutor on the Resources page of this website, which you can access using the link in the navigation bar.
12 points total; 4 points each part; individual-only
Create a method that takes an array of integers called data
and
an integer value
and uses a loop to determine if value
is in
the array. The method should return true
if value
is in the
array and false
if it is not in the array. Your code should
work for an array of any length, although you may assume that it
has at least one element.
Consider the following class constant, which is an array containing strings representing abbreviations of the schools/divisions at Harvard:
public static final String[] SCHOOL_ABBREVS = {"FAS", "GSAS", "DCE", "SEAS", "HLS", "HMS", "HBS", "HGSE", "HSPH", "GSD", "HDS"};
Create a method named schoolNumber
that takes as a parameter a
single String
representing a school abbreviation and returns
the index of that school abbreviation in the SCHOOL_ABBREVS
array. If the string passed in as a parameter does not appear in
the array, the method should return -1. For example:
schoolNumber("FAS")
should return 0, because "FAS"
has
an index of 0 in the array.schoolNumber("HLS")
should return 4, because "HLS"
has
an index of 4 in the array.schoolNumber("FOO")
should return -1, because "FOO"
does
not appear in the array.Your method should use a for
loop to traverse the
SCHOOL_ABBREVS
array looking for a match for the abbreviation
passed in as a parameter. If it finds a match, it should return
the index of that abbreviation. If it never finds a match, it
should return -1. You are NOT allowed to use any helper
methods from the Arrays
class to implement this method. Don’t
forget that you need to use the equals
method to determine if
two strings are equal. Also, note that you do not need to
pass in the SCHOOL_ABBREVS
array as a parameter, because it is a
class constant that is accessible in any method.
Complete the following method to make it negate all of the
elements of the array data
. For example, if data
refers to
the array {1, -2, -5, 4, -9}
, calling negate(data)
should
change the array to be {-1, 2, 5, -4, 9}
. Use as many lines as
are needed to perform this task. Your code should work for an
array of any length.
public static void negate(int[] data) { }
Note that the method has a return type of void
. That is
because the method is passed a copy of a reference to the
array, not a copy of the array itself, and thus any changes that
the method makes to the array will still be visible after the
method returns.
10 points total; individual-only
Assume that you have a variable twoD
that refers to a
two-dimensional array of integers. For example, here is one example
of such an array:
int[][] twoD = {{1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12}};
(2 points) Write an assignment statement that replaces the value of 7 in the above array with a value of 15.
(4 points) Write a code fragment that prints the values in the
leftmost column of the array to which twoD
refers. Print each
value on its own line. For the array above, the following values
would be printed:
1 5 9
Make your code general enough to work on any two-dimensional
array named twoD
that has at least one value in each row.
You may also assume that the array to which twoD
refers is
not “ragged” — i.e., that all rows have the same number
of elements.
(4 points) Write a code fragment that determines how many odd
integers there are in the array to which twoD
refers and prints
that count. For the array above, the method would print 6, because
there are 6 odd integers.
Make your code general enough to work on any two-dimensional
array named twoD
that has at least one row.
12 points total; individual-only
Consider the following simple recursive method:
public static void printPattern(int m, int n) { if (m == n) { return; } if (m < n) { System.out.print("("); printPattern(m + 1, n); System.out.print("\\"); /* prints a single backslash */ } else { System.out.print("/"); printPattern(m, n + 1); System.out.print(")"); } }
To experiment with this method, you should take the following steps:
Problems4and5.java
into your
ps4
folder and open the folder in VS Code. You should see a class
named Problems4and5
that contains the printPattern
method
and some other code.Add a main
method that includes at least one test call to
printPattern
. You should follow each test call with an
empty println
statement to add a newline character
at the end of the characters printed by the method.
For example:
printPattern(2, 6); System.out.println();
Try several different parameters and see what output is produced in each case.
Then answer the following questions:
printPattern(1, 4)
. Use
indentation to indicate which statements are performed by a
given invocation of the method, following the approach used in
the lecture notes to trace printSeries
. (See the slide
entitled “Tracing a Recursive Method” on page 3 of the notes on
recursion.)///)))
for a given combination of initial parameters, the revised method
should print )))///
for that same set of initial parameters.8 points total; individual-only
Consider the following recursive method:
public static int mystery(int a, int b) { if (a * b == 0) { return a; } else { int myst_rest = mystery(a - 1, b - 2); return b + myst_rest; } }
This method is also included in the Problems4and5
class mentioned
in the previous problem.
(2 points) Trace the execution of mystery(5, 6)
.
To do so, complete the template that we have provided in section 5-1
of ps4_partI
. In particular, you should:
Include a separate “frame” for each call. We have filled in
some of the components of the frames for the first two calls
for you. You should replace each ...
with the appropriate
integer, and you should add frames for additional calls as
needed, until you reach the base case.
Begin each frame with lines that explicitly state the values assigned to the parameters, as we have done for the first call.
Next, if the call is a base case, you can simply show the
value that is returned (omitting the line for myst_rest
). If
the call is a recursive case, you should show the recursive
call on the line for myst_rest.
Once you have reached the base case, you should work your way
back through the frames for the previous calls. Add in both
the results of the recursive call (i.e, the value assigned to
myst_rest
) and the value returned by the call itself.
(1 point) What is the value returned by mystery(5, 6)
? To
check your answer, you can add a test call to your main
method.
For example:
System.out.println("mystery(5, 6) returns " + mystery(5, 6));
(2 points) During the execution of mystery(5, 6)
, method
frames are added and then removed from the stack. How many
method frames are on the stack when the base case is reached?
You should assume that the initial call to mystery(5, 6)
is
made from within the main
method, and you should include the
stack frame for main
in your count.
(3 points) Give an example of values of a
and b
that would
produce infinite recursion, and explain why it would occur.
Submit your ps4_partI.pdf
file by taking the following steps:
If you still need to create a PDF file, open your file on Google Drive, choose File->Download->PDF document, and save the PDF file on your machine.
Click on the name of the assignment in the list of assignments on Gradescope. You should see a pop-up window labeled Submit Assignment. (If you don’t see it, click the Submit or Resubmit button at the bottom of the page.)
Choose the Submit PDF option, and then click the Select PDF button and find the PDF file that you created. Then click the Upload PDF button.
You should see a question outline along with thumbnails of the pages from your uploaded PDF. For each question in the outline:
As you do so, click on the magnifying glass icon for each page and doublecheck that the pages that you see contain the work that you want us to grade.
Once you have assigned pages to all of the problems in the question outline, click the Submit button in the lower-right corner of the window. You should see a box saying that your submission was successful.
Important
It is your responsibility to ensure that the correct version of every file is on Gradescope before the final deadline. We will not accept any file after the submission window for a given assignment has closed, so please check your submissions carefully using the steps outlined above.
If you are unable to access Gradescope and there is enough
time to do so, wait an hour or two and then try again. If you
are unable to submit and it is close to the deadline, email
your homework before the deadline to
cscis111-staff@lists.fas.harvard.edu
50-60 points total
Begin by downloading the following zip file: ps4_part2.zip
Unzip this archive, and you should find a folder named ps4_part2
, and
within it the files you will need for Part II.
Keep all of the files in the ps4_part2
folder, and open that folder in
VS Code using the File->Open Folder or File->Open menu option.
30 points; individual-only
Background information
Your task is to create a simple database program that provides access
to historical population data for the most populous cities in the
United States. The source of our data is a site called
peakbagger.com.
Format of the data file
Your program will read from a file like
cities.txt
— a comma-delimited
text file that contains the information needed for the database. We
have included this file in the ps4_part2
folder that you should
already have downloaded (see above).
Each line of the file represents one record of the database, and each record has information about a particular city’s population in a particular year.
More specifically, each record has the following five fields:
33.1
represents a
total population of 33,100
. (Strictly speaking, these
populations are for the metropolitan areas surrounding each city.)Because the file is a CSV file, the individual fields are separated by commas.
For example, here are the first three lines of the file that we’ve given you:
1790,1,Philadelphia,PA,44.1 1790,2,New York,NY,33.1 1790,3,Boston,MA,18.3
All of these records are for the year 1790. The first record indicates that the city with the largest population in 1790 (and thus a rank of 1) was Philadelphia, PA, which had a population of 44,100. The second record indicates that the city with the second largest population in 1790 (and thus a rank of 2) was New York, NY, which had a population of 33,100. The third record indicates that the city with the third largest population in 1790 (and thus a rank of 3) was Boston, MA, which had a population of 18,300.
Important: In cities.txt
, the results are arranged from oldest
to most recent, starting from 1790 and including results at 20-year
intervals up to and including 2010 (i.e., 1790, 1810, 1830, ..., 1970,
1990, 2010). Within a given year’s results, the results are arranged
according to rank, starting with a rank of 1 and continuing to a
rank of 20. Your program should be able to handle other files
that include populations from different years. In addition, your code
for at least one of operations should not make any assumptions
about the order of the records. See below for more detail.
Required functionality
Your program should allow the user to perform the following operations:
Search by city: Given a city and state entered by the user, the program should find all records in the file for that city/state combination and output the year, rank, and population from each matching record. The results should be in chronological order from oldest to most recent. For example:
city: Boston state abbreviation: MA 1790 3 18,300 1810 4 38,700 1830 3 85,600 1850 3 308,000 1870 3 501,000 1890 4 818,000 1910 4 1,213,000 1930 6 1,479,000 1950 6 2,301,000 1970 7 2,703,000 1990 9 3,355,000 2010 11 4,407,000
If the user enters a city/state combination that is not found in the database, the program should print a message to that effect (see the sample run).
For this type of search, you may assume that the records in the data file for a given city are already ordered by year, and thus you can simply print the results in the order in which you encounter them in the file.
Search by year: Given a year entered by the user, the program the program should find all records in the file for that year and output the rank, population, city and state from each matching record. The results should printed in order of increasing rank. For example:
year: 1790 1 44,100 Philadelphia, PA 2 33,100 New York, NY 3 18,300 Boston, MA 4 16,400 Charleston, SC 5 13,600 Salem, MA 6 13,500 Baltimore, MD 7 6,700 Newport, RI 8 6,400 Providence, RI 9 5,300 Gloucester, MA 10 4,800 Newburyport, MA 11 4,700 Portsmouth, NH 12 4,600 Nantucket, MA 13 4,500 Middleborough, MA 14 4,500 New Haven, CT 15 3,800 Richmond, VA 16 3,500 Albany, NY 17 3,000 Norfolk, VA 18 2,800 Petersburg, VA 19 2,800 Alexandria, VA 20 2,700 Hartford, CT
If the user enters a year that is not found in the database, the program should print a message to that effect (see the sample run).
For full credit, you should not assume that the records for a given year are stored in order of increasing rank, and thus you should not print the results in the order in which you encounter them in the file. Rather, you should initially store the matching records in an array, and then later use the array to print the results in order of increasing rank. See the guidelines below for more detail.
Other functionality details
Your program should begin by asking the user for the name of the data file. Next, your program should repeatedly offer the user the following menu of options:
1. search by city 2. search by year 3. quit Enter your choice (1-3):
and read in the user’s choice as an integer. Our starter code includes these components of the program.
If the user enters 1 or 2, the program should prompt for and read the necessary information from the user, perform the requested operation, and display the menu again. This should continue until the user enters 3, at which point the program should end.
In general, your program should behave in ways that are consistent with the sample run that is available here.
In addition, your code must follow the additional guidelines provided below.
Starter code
In the ps4_part2
folder that we’ve provided (see above), we’ve
included a file named CityDatabase.java
that includes some
starter code for this problem.
When you open this file in VS Code, you should see:
a class constant named NUM_CITIES_PER_YEAR
, which is an
integer specifying how many cities are included for each year.
In our default version, the value of this constant is 20, because
we have 20 cities for each year. However, your code should be
able to adjust to situations in which this value is changed.
For example, if we decided to use files with 25 cities for each
year, the only change that should be necessary is to change the
value of this constant to 25.
a method called getChoice
that you should use to display the
menu of options and read in the user’s choice. This method
returns the user’s choice as an integer.
a method called convertPop
that you should use when converting a
String
population field into an integer that
represents the corresponding population. For example, the call
convertPop("44.1")
will return the integer 44100
.
the beginnings of a main
method; you should fill in the
necessary code for each of the three possible choices that the
user can make.
Other implementation guidelines
For full credit on option 2 (search by year), you should not assume that the records for a given year are in order of increasing rank, and thus you should not print the results in the order in which you encounter them in the file. Rather, you should initially store the records in an array, and then later use the array to print the results in order of increasing rank.
Before you begin reading through the file, create an array that is large enough to store all of the records for a given year. (The class constant that we have given you may be useful in this regard!) Then, every time that you encounter a record whose year field matches the year that you are searching for, store the record in the array at the position specified by the rank component of the record. For example, when you encounter the record for the specified year with a rank of 5, store that record in position 5 of the array.
Once you have finished reading the file, you can then traverse the array and print the results in order of increasing rank.
To test whether your program works correctly for files in which
the records are not in order of increasing rank, you can use the
following file: cities2.txt
This file is available in the ps4_part2
folder.
Use static methods to capture the structure of your program and
to eliminate code duplication. You are required to have at
least two useful static methods besides main
, getChoice
, and
convertPop
, and at least one of your methods should take an
array as a parameter. One easy way to create a method with an
array parameter is to use a separate method to print the
array of results that you are using in option 2.
Reading from the console: In the main
method, we have created
a single Scanner
object for reading from the console,
and you should pass it as a parameter to any method that needs
to read a value from the user. You will lose points if your
program creates more than one Scanner
object for reading from
the console.
Use the Scanner
object’s nextLine()
method to read in
the information that the user specifies for a given search
(the city and the state abbreviation for option 1,
and the year for option 2). Do not use the next()
or
nextInt()
methods in code that you write.
Reading from the file: You will also use Scanner
objects to read
from the data file. Use the approach shown in lecture in which
you read in one line at a time from the file and use the
String.split()
method to break the line into an array of
strings, one string for each field. Put all file-reading code in
one or more separate methods, and create the necessary Scanner
object inside each of those methods.
Remember that you can convert fields from strings to integers using
the Integer.parseInt()
method. For example, if you use the
name fields
for the array returned by the split
method, then
fields[1]
will be a string representation of the rank
(the second field on the line). To convert it to an integer that
is stored in a variable named rank
, you can do the
following:
int rank = Integer.parseInt(fields[1]);
Efficiency: For full credit, a given search (i.e., a given search by city or a given search by year) should perform only a single pass through the file. You should not need to read through the file multiple times in order to complete a single search operation.
Formatting the output: When printing the results of a search:
Use a tab character ('\t'
) to create the columns in the
output. For example, in the output for option 1, put one tab
character between the year and rank values, and another tab
character between the rank and population values.
When printing an integer population value (obtained using
the convertPop
method that we’ve given you), use the
following printf
format string: "%,10d\n"
. This will print
the populations with commas in the right places, and it will
also line up the population values with each other. For example,
if the population is stored in a variable called pop
, you
would use the following printf
statement to print it:
System.out.printf("%,10d\n", pop);
Limit yourself to the concepts we have covered in lecture.
Employ good programming style. See the coding conventions for more detail.
10 points; individual-only; required of grad-credit students; may be completed by other students for “partial” extra credit
Extend your CityDatabase
program from problem 6 so that it can also
handle data files in which the results are not ordered by year. To get
started, use the File->Save As menu option to make a copy of your
existing CityDatabase.java
file, calling it
CityDatabase2.java
. Make sure to also rename the class accordingly.
Because the results are no longer guaranteed to be ordered by year, you will need to rework your code for option 1 (search by city) so that it does not print the results in the order in which you encounter them in the file. Rather, you should initially store the results in an array, and then later use the array to print the results in order of increasing rank.
In Problem 6, we took a similar approach for option 2 (search by year). In that case, we were able to use the rank of the record to determine where the record should be stored in the array. In this problem, however, we need to arrange the results for option 1 by year, and thus our approach will need to be a bit different.
Your extended program should be able to handle data files like the
following: cities3.txt
This file is also available in the ps4_part2
folder.
You should assume that the first line of the data file is a special header line that specifies the years for which population counts have been recorded. In the file that we have given you, the header line looks like this:
1790,1810,1830,1850,1870,1890,1910,1930,1950,1970,1990,2010
In your code for both types of searches, you should read this special header line first, before you begin the file-reading loop. You can do so using a line like the following:
String header = input.nextLine();
In your code for option 2 (search by year), you can simply ignore the header after you read it in, because searches by year do not need to use the information in the header.
However, in your code for option 1 (search by city), you should use
the split()
method to split the header into an array of year
strings. Next, create a results array that has the same length as the
array of year strings. In the example above, the header has 12 years,
and thus you would end up creating an array of length 12 for the results.
Then, every time that you encounter a record whose city and state
fields match the values that you’re searching for, store the record in
the results array at a position that is based on the record’s year
field. For example, when you encounter the record for the specified
city and state with a year field of "1830"
, you would store that
record in position 2 of the array, because "1830"
is the third year
in the header (the one in position 2 of the years array).
Once you have finished reading the file, you can then traverse the
array and print the results in order of increasing year. Because a
given city may not have results for every year, you may end up with
null
values for some of the elements of the results array, and you
should skip over all such elements when printing the results.
20 points; 5 pts. each part; pair-optional
In this problem, you will write several static methods that use
recursion to operate on a String
.
Requirements:
for
, while
, or do-while
loops) is not
allowed.String
methods that you may use are
charAt
, length
, equals
, and substring
. No use of other
String
methods is allowed. In addition, make sure to follow
any additional restrictions specified in the problem.Getting started
In the ps4_part2
folder, we have given you a file named
StringRecursion.java
that you should use for your work on
this problem.
When you open this file in VS Code, you’ll notice that the provided class already includes:
one example of a recursive string method —
the numOccur
method that we covered in lecture.
the beginnings of a main
method, with two test calls for
numOccur
; we encourage you to add test calls for your own
methods to main
, although doing so is not required.
The methods
Add to StringRecursion
your implementations of the following
methods, using the headers that are specified:
public static void printEveryOther(String str)
This method must use recursion to print every other character
in the string str
. For example, a call of
printEveryOther("method")
should produce the following output:
mto
The method should not return a value.
Special cases: If the value null
or the empty string (""
)
are passed in as the parameter, the method should just print a
newline (by executing the statement System.out.println();
).
Hints:
public static void printTriangle(String str)
This method must use recursion to print a “triangle” that
consists of increasingly longer substrings of the string str
.
For example, a call of printTriangle("hello")
should produce
the following output:
h he hel hell hello
The method should not return a value.
Special cases: If the value null
or the empty string (""
)
are passed in as the parameter, the method should just return
without printing anything.
Hint: When constructing the parameter for the recursive call, you may find it helpful to take a slightly different approach than the one that we have typically used when processing strings recursively.
public static boolean contains(String str, char ch)
This method must use recursion to determine if the string
specified by the parameter str
contains at least one
occurrence of the character specified by the parameter ch
,
returning true
if it does and false
otherwise. For example:
contains("recursion", 's')
should return true
contains("recursion", 'z')
should should return false
.This method should not do any printing; it should simply
return the appropriate boolean value. In addition, this method
may not call the numOccur
method that we have given you.
Special cases: If the value null
or the empty string (""
)
are passed in for the first parameter, the method should return
false
.
public static int lastIndexOf(char ch, String str)
This method must use recursion to find and return the index
of the last occurrence of the character ch
in the string
str
, or -1
if ch
does not occur in str
. For example:
lastIndexOf('r', "recurse")
should return 4
lastIndexOf('p', "recurse")
should return -1
This method should not do any printing; it should simply return the appropriate integer.
Special cases: If the value null
or the empty string (""
)
are passed in for the second parameter, the method should return
-1
.
Hint: Here again, when constructing the parameter for the recursive call, you may find it helpful to take a slightly different approach than the one that we have typically used when processing strings recursively.
Make sure that your methods meet all of the requirements listed at the start of the problem.
Important
If you chose to work on Problem 8 with a partner, both you and your partner should submit your own copy of your joint work, along with your individual work on the other two problems.
You should submit only the following files:
CityDatabase.java
CityDatabase2.java
(if you completed Problem 7)StringRecursion.java
Here are the steps:
Click on the name of the assignment in the list of assignments. You should see a pop-up window with a box labeled DRAG & DROP. (If you don’t see it, click the Submit or Resubmit button at the bottom of the page.)
Add your files to the box labeled DRAG & DROP. You can either drag and drop the files from their folder into the box, or you can click on the box itself and browse for the files.
Click the Upload button.
You should see a box saying that your submission was successful.
Click the (x)
button to close that box.
The Autograder will perform some tests on your file. Once it is done, check the results to ensure that the tests were passed. If one or more of the tests did not pass, the name of that test will be in red, and there should be a message describing the failure. Based on those messages, make any necessary changes. Feel free to ask a staff member for help.
Note: You will not see a complete Autograder score when you submit. That is because additional tests will be run later, after the final deadline for the submission has passed. For such problems, it is important to realize that passing all of the initial tests does not necessarily mean that you will ultimately get full credit on the problem. You should always run your own tests to convince yourself that the logic of your solutions is correct.
If needed, use the Resubmit button at the bottom of the page to resubmit your work. Important: Every time that you make a submission, you should submit all of the files for that Gradescope assignment, even if some of them have not changed since your last submission.
Near the top of the page, click on the box labeled Code. Then click on the name of each file to view its contents. Check to make sure that you see the code that you want us to grade.
Important
It is your responsibility to ensure that the correct version of every file is on Gradescope before the final deadline. We will not accept any file after the submission window for a given assignment has closed, so please check your submissions carefully using the steps outlined above.
If you are unable to access Gradescope and there is enough
time to do so, wait an hour or two and then try again. If you
are unable to submit and it is close to the deadline, email
your homework before the deadline to
cscis111-staff@lists.fas.harvard.edu
Last updated on July 9, 2025.