Old version
This is the CS 112 site as it appeared on May 11, 2018.
Problem Set 6
due by 11:59 p.m. on Tuesday, April 3, 2018
Preliminaries
In your work on this assignment, make sure to abide by the collaboration policies of the course.
If you have questions while working on this assignment, please
come to office hours, post them on Piazza, or email
cs112-staff@cs.bu.edu
.
Make sure to submit your work on Apollo, following the procedures found at the end of the assignment.
Part I
35 points total
Problem 1: Choosing an appropriate list implementation
12 pts. total; 4 pts. each part; individual-only
Put all of your work for this problem in a plain-text file named
ps6pr1.txt
.
In lecture, we considered two different implementations of the list
ADT: ArrayList
and LLList
. For each of the following applications,
decide which list implementation would be better for that particular
application. Your should consider both time efficiency and space
efficiency, and you should assume that both implementations have an
associated iterator like the LLListIterator
that we discussed in
lecture. Explain your answers.
-
You need to store information about the gardening tools that you sell in your online store. To do so, you maintain a list of product records, each of which contains information about a single type of product, including how many copies of it you have in stock. You don’t tend to change the types of tools that you sell, and thus items are seldom added or removed from the list. However, you need to update a product’s record whenever your inventory of that product changes (e.g., because someone purchases one!). A product’s position in the list is also its product ID, and therefore updating a record involves using the product ID to get the corresponding record and modifying its contents.
-
You need to maintain a list of tweets that are made by government officials in the current week. The number of tweets can vary widely from week to week in a way that is hard to predict. You start with an empty list at the start of the week, and as tweets are made you add them to the list. You need to frequently display the tweets in reverse chronological order – from most recent to least recent.
-
You are responsible for maintaining a monthly list of events for an online arts calendar. The number of events is fairly consistent from month to month. Events are added to the list in chronological order, and you need to display them in that same order.
Problem 2: Improving the efficiency of an algorithm
13 pts. total; individual-only
Put all of your work for this problem in a plain-text file named
ps6pr2.txt
.
Consider the following method, which takes two unsorted lists, list1
and list2
– both instances of our LLList
class from lecture – and
creates a third list containing the intersection of list1
and list2
:
public static LLList intersect(LLList list1, LLList list2) { LLList inters = new LLList(); for (int i = 0; i < list1.length(); i++) { Object item1 = list1.getItem(i); for (int j = 0; j < list2.length(); j++) { Object item2 = list2.getItem(j); if (item2.equals(item1)) { inters.addItem(item2, inters.length()); break; // move onto the next item from list1 } } } return inters; }
The lists are unsorted, so we take a nested-loop approach in which
each item in list1
is compared to the items in list2
; if a match is
found in list2
, the item is added to the intersection and we break out
of the inner loop. (Note that this method will include duplicate
values in the intersection when list1
itself has duplicates; doing so
is acceptable for the purposes of this problem.)
-
(3 points) What is the worst-case running time of this algorithm as a function of the length m of
list1
and the length n oflist2
? Don’t forget to take into account the time efficiency of the underlying list operations,getItem()
andaddItem()
. Use big-O notation, and explain your answer briefly. -
(7 points) Rewrite this method to improve its time efficiency. Your new method should not modify the existing lists in any way, and it should use no more than a constant (O(1)) amount of additional memory. Make the new method as efficient time-wise as possible, given the constraints on memory usage. You should assume this method is not part of the
LLList
class, and therefore it doesn’t have direct access to the privateLLList
members. -
(3 points) What is the worst-case running time of the improved algorithm? Use big-O notation, and explain your answer briefly.
Problem 3: Working with stacks and queues
10 points; 5 points each part; individual-only
Put all of your work for this problem in a plain-text file named
ps6pr3.txt
.
-
Write a method
remAllStack(Stack<Object> stack, Object item)
that takes a stack and an item, and that removes all occurrences of that item from the stack. After the removals, the remaining items should still be in the same order with respect to each other. For example, if you have a stack that contains (from top to bottom) {5, 2, 7, 2, 10}, if you use the method to remove all occurrences of 2, the resulting stack should contain (from top to bottom) {5, 7, 10}.Important guidelines:
-
Your method may use either another stack or a queue to assist it. It may not use an array, linked list, or other data structure. When choosing between a stack or a queue, choose the one that leads to the more efficient implementation.
-
You should assume that the method does not have access to the internals of the collection objects, and thus you can only interact with them using the methods in the interfaces that we discussed in lecture.
-
Although you aren’t writing this method as part of a class, you should use appropriate Java syntax. The methods should be static and should not return a value.
-
-
Write a method
remAllQueue(Queue<Object> queue, Object item)
that takes a queue and an item, and that removes all occurrences of that item from the queue. After the removals, the remaining items should still be in the same order with respect to each other. For example, if you have a queue that contains (from front to rear) {5, 2, 7, 2, 10} and you use the method to remove all occurrences of 2, the resulting queue should contain (from front to rear) {5, 7, 10}. The same guidelines that we specified forremAllStack()
also apply here.
Part II
65 points total
Preparing for Part II
Begin by downloading the following zip file: ps6.zip
Unzip this archive, and you should find a folder named ps6
, and
within it the files you will need for Part II. Make sure that you
keep all of the files in the same folder.
Important: When you compile the code in this folder, you may see one or more warnings labeled “unchecked cast”. You can safely ignore them.
Problem 4: Removing all occurrences from a list
20 points; individual-only
If you haven’t already done so, complete the steps above to prepare for this and the remaining problems in Part II.
Assume that we want list objects to support the following method:
boolean removeAll(Object item)
This method takes an item, and it should removes all occurrences
of that item from the list. If one or more occurrences of the item are
removed, the method should return true
. If there were no occurrences
of the item to begin with, it should return false
.
Create two implementations of this method: one as part of the
ArrayList
class, and one as part of the LLList
class. Both classes
can be found in the ps6
folder.
Important: For full credit, both methods should:
- run in O(n) time, where n is the number of items in the list
- use no more than a constant (O(1)) amount of additional memory.
In addition, you should make sure that your ArrayList
version of the method doesn’t leave any extraneous references to
objects in the items
array. For example, let’s say that
you have 9 items in the ArrayList
. If a call to removeAll()
removes
3 of them, you should end up with an array in which the first 6 elements
hold references to actual items, and all of the remaining elements
are null
.
To facilitate testing, we have added a constructor to both classes
that takes an array of type Object
containing the initial contents
of the list. Given these constructors, you can test your methods using
examples that look like this:
> String[] letters = {"a", "b", "c", "a", "c", "d", "e", "a"}; > ArrayList list1 = new ArrayList(letters); > list1 {a, b, c, a, c, d, e, a} > list1.removeAll("a") true > list1 {b, c, c, d, e} > list1.removeAll("c") true > list1 {b, d, e} > list1.removeAll("x") false > list1 {b, d, e} > LLList list2 = new LLList(letters); > list2 {a, b, c, a, c, d, e, a} > list2.removeAll("a") true > list2 {b, c, c, d, e} > list2.removeAll("x") false
Problem 5: Implementing the Bag ADT using a linked list
25 points; pair-optional
This is the only problem of the assignment that you may complete with a partner. See the rules for working with a partner on pair-optional problems for details about how this type of collaboration must be structured.
Earlier in the course, we worked with the ArrayBag
class. This class
can be seen as an implementation of the Bag abstract data type, which
we can specify in Java using the following interface:
public interface Bag { boolean add(Object item); boolean remove(Object item); boolean contains(Object item); int numItems(); Object grab(); Object[] toArray(); }
In the ps6
folder, we have included:
- the
Bag
interface (Bag.java
) - a version of the
ArrayBag
class that includes animplements
clause in its header to specify that it implements this interface.
In a file named LLBag.java
, write a class called LLBag
that
implements the Bag
interface using a linked list instead of an
array. You may use a linked list with or without a dummy head node.
In designing your implementation, you may find it helpful to compare
the LLList
class, our linked-list implementation of the List
ADT,
to the ArrayList
class, our array-based implementation of
that same ADT. In the same way that LLList
uses a linked list
instead of an array to implement a list, your LLBag
class should use
a linked list instead of an array to implement a bag.
Notes:
-
Make sure that your class explicitly indicates that it implements the
Bag
interface. To allow your new class to compile, you will need to put it in theps6
folder withBag.java
. -
Like the
LLList
class, yourLLBag
class should use a private inner class calledNode
for the nodes in the linked list. -
In addition to the methods in the
Bag
interface, yourLLBag
class should also have atoString()
method that overrides the defaulttoString()
method inherited from theObject
class. Consult theArrayBag
toString()
method to see what the string returned by this method should look like. -
One of the benefits of using a linked list is that there is no need to specify a maximum size—the bag can effectively grow without limit. Therefore, you will not need to provide a constructor that specifies a maximum size, and your
add()
method can always returntrue
. -
Because the order of the items in a bag doesn’t matter, you can add items to either end of the linked list; however, make sure that your method adds items in O(1) time.
-
In general, you should make your methods as efficient as possible. For example, when implementing the
remove()
method, you should make sure that you don’t traverse the list more than once. -
Copy over the
main()
method from theArrayBag
class, and make whatever modifications are necessary to allow it to test yourLLBag
class.Important: If you compare the results that you obtain from testing the two classes, you may see that the items are not in the same order in both sets of tests. That is to be expected, and it’s not a problem because items in a bag do not have a position!
Problem 6: Palindrome tester
20 points; individual-only
-
(15 points) A palindrome is a string like
"radar"
,"racecar"
, and"abba"
that reads the same in either direction. To enable longer palindromes, we can ignore spaces, punctuation, and the cases of the letters. For example:"A man, a plan, a canal, Panama!"
is a palindrome, because if we ignore spaces and punctuation and convert everything to lowercase we get
"amanaplanacanalpanama"
which is a palindrome.
In the file
Palindrome.java
that we’ve included in theps6
folder, add a static method calledisPal()
that takes aString
object as a parameter and determines if it is a palindrome, returningtrue
if it is andfalse
if it is not.A string of length 1 and an empty string should both be considered palindromes. Throw an exception for
null
values.Although this problem could be solved using recursion or an appropriately constructed loop that manipulates the
String
object directly, your method must use an instance of one or more of the following collection classes from theps6
folder:ArrayStack
LLStack
ArrayQueue
LLQueue
You must not use any other data structure, including arrays or linked lists other than the ones that are “inside” instances of the above collections. Rather, you should put individual characters from the original string into an instance of one or more of the above collections, and use those collection object(s) to determine if the string is a palindrome.
For full credit, you should:
-
Write your method so that spaces, punctuation, and the cases of the letters don’t prevent a string from being a palindrome. To put it another way, make sure that your method only considers characters in the string that are letters of the alphabet and that it ignores the cases of the letters. See our example above.
-
Make your method as efficient as possible. In particular:
-
You should perform only one iteration over the original
String
object. After that one iteration, any additional manipulations of the characters should be done using the collection object(s) that you have chosen to use. -
Because we want to avoid unnecessary scanning, you may not use any of the built-in
String
methods exceptcharAt()
andlength()
.
-
Hints:
-
When constructing the collection object(s) that you decide to use, you will need to specify the appropriate data type for the items in the collection. Because
char
values are primitives, you should use the corresponding “wrapper” class, which is calledCharacter
. -
You may also find it helpful to use the
Character.toLowerCase()
orCharacter.toUpperCase()
method. -
Remember that
char
values are essentially integers, so you can compare them just as you would compare integers.
-
(5 points) For professional programmers, writing well-formatted units tests is an extremely important part of their work. In the
main()
method ofPalindrome.java
, we’ve given you an example of what such a unit test should look like.Add five additional unit tests for your
isPal()
method to themain()
method. Your unit tests must follow the same format as our example test. In particular, the output of each of your unit tests should include:- a header that specifies the test number and a description of what is being tested
- the actual return value that you get from that test
- the expected return value
- whether the actual return value matches the expected return value.
Put each test in the context of a
try-catch
block so that you can handle any exceptions that are thrown. Leave a blank line between tests.
Submitting Your Work
You should use Apollo to submit the following files:
- your
ps6pr1.txt
file for Problem 1 - your
ps6pr2.txt
file for Problem 2 - your
ps6pr3.txt
file for Problem 3 - your modified
ArrayList.java
andLLList.java
files for Problem 4 - your
LLBag.java
file for Problem 5; if you worked with a partner, make sure that you each submit a copy of your joint work, and that you include comments at the top of each file specifying the name and email of your partner - your modified
Palindrome.java
file for Problem 6
Warnings
-
Make sure to use these exact file names, or Apollo will not accept your files. If Apollo reports that a file does not have the correct name, you should rename the file using the name listed in the assignment or on the Apollo upload page.
-
Make sure that you do not try to submit a
.class
file or a file with a~
character at the end of its name. -
Before submitting your files, make sure that your BU username is visible at the top of the Apollo page. If you don’t see your username, click the Log out button and login again.
-
If you make any last-minute changes to one of your Java files (e.g., adding additional comments), you should compile and run the file in DrJava after you make the changes to ensure that it still runs correctly. Even seemingly minor changes can cause your code to become unrunnable.
-
If you encounter problems with Apollo, click the Log Out button (if you are already logged in), close your browser, and try again. If possible, you may also want to wait an hour or two before retrying. If you are unable to submit and it is close to the deadline, email your homework before the deadline to
cs112-staff@cs.bu.edu
Here are the steps:
- Login to Apollo, using the link in the left-hand navigation bar. You will need to use your Kerberos user name and password.
- Check to see that your BU username is at the top of the Apollo page. If it isn’t, click the Log out button and login again.
- Find the appropriate problem set section on the main page and click Upload files.
- For each file that you want to submit, find the matching upload section for the file. Make sure that you use the right section for each file. You may upload any number of files at a time.
- Click the Upload button at the bottom of the page.
- Review the upload results. If Apollo reports any issues, return to the upload page by clicking the link at the top of the results page, and try the upload again, per Apollo’s advice.
- Once all of your files have been successfully uploaded, return to the upload page by clicking the link at the top of the results page. The upload page will show you when you uploaded each file, and it will give you a way to view or download the uploaded file. Click on the link for each file so that you can ensure that you submitted the correct file.
Warning
Apollo will automatically close submissions for a given file when its final deadline has passed. We will not accept any file after Apollo has disabled its upload form, so please check your submission carefully following the instructions in Step 7 above.