Due Monday, October 7 @ 8:00pm on Gradescope
In this assignment, you’ll be constructing two different kinds of linked lists: A singly linked list and a doubly linked list. The singly linked list is very similar to the one we developed in class. When you’re done, you’ll have a pretty good start on two different fully-fledged generic linked list classes!
Download a pre-prepared Eclipse Project hw3.zip
and import it into Eclipse using these instructions. You will make all of your changes inside the Java files in this project.
For this assignment you have 5 submissions. That is not a lot for an assignment of this size, so you will need to test thoroughly before submission. After you have used your submissions, you may continue to submit, but your submission will be penalized 4 points for every extra submission. (So, your 6th submission receives a -4, your 7th submission a -8, etc.)
Please note that you will likely need to use one of those submissions to properly determine the exact exceptions that you will need to raise in error situations. You may also need some if your efficiency is borderline and you need to submit a few times. Make sure to save submissions for these purposes.
The first part of this assignment is to build a singly linked list, as described in class. You are free to use any of the demo code we wrote in class to help you here. (We wrote some of these methods in class...) The methods you need to write are as follows:
public SinglyLinkedList()
A basic constructor.
public String toString()
A basic toString().
public void addHead(ListType value)
Add an item to the head of the list.
value
— The value to add to the list
public void add(ListType value)
Add an item to the end of the list.
value
— The value to be added to the list
public int size()
Determine how many items are in the list
public boolean isEmpty()
Determine whether or not the list is empty.
public boolean contains(ListType value)
Determine if a specified value is in the list.
Parameters: value
— The value to search for
Returns: true if the item is in the list and false otherwise
private SingleListNode<ListType> nodeAtIndex(int index)
Optional Helper Function: Get the node at the position in the list specified by index.
Throws an IndexOutOfBoundsException with an appropriate message if index is out of range.
Parameters: index
— The index of the item whose node is wanted
Returns: The node at the provided index
public ListType get(int index)
Get an item at a specified index from the list.
Throws an IndexOutOfBoundsException with an appropriate message if index is out of range.
Parameters: index
— The numeric index (starting from 0) of the item to get
Returns: The item
public void add(int index, ListType value)
Adds a node containing value at the specified position in the list, if such a position can be added to the list.
Throws an IndexOutOfBoundsException with an appropriate message if index is out of range.
Parameters:
index
— The index to add the new value at
value
— The value to add to the list
public ListType remove(SingleListNode<ListType> node)
Remove the specified node from the list.
Throws a NoSuchElementException (with no extra message) if the node is not in the list.
Parameters: node
— The node to remove from the list
Returns: The item contained in this node
public void remove(ListType value)
Remove the first instance of an item with a given value from the list.
Throws a NoSuchElementException (with no extra message) if the node is not in the list.
value
— The value of the item to remove.
public ListType remove(int index)
Remove the node at the position in the list specified by index, if such a position is in the list, and returns the data value that was at that location.
Throws an IndexOutOfBoundsException with an appropriate message if index is out of range.
Parameters: index
— The index of the item to remove
Returns: The item that was removed
public void removeLast(ListType target)
Removes the node containing the last occurrence of a value in the list.
Throws a NoSuchElementException if the node is not in the list.
target
— The item to remove from the list
public void removeAll(ListType value)
This method removes every node in the list that contains a particular value. Realize that the target can occur multiple times and anywhere in the list! This method does nothing if the target is not in the list.
This method must run in linear time, O(N), or you will lose points!
value
— The value of the item to remove
The second part of this assignment is to build a doubly linked list, a list where each node has both a next and and a previous pointer:
This allows for a few optimizations:
If you have a pointer to the tail of the list, you can traverse it backward.
If you have a pointer to a node in the list, you can remove it from the list without needing to traverse the list.
If you have a pointer to a node in the list, you can quickly add a new item before or after it.
The new methods you need to write are as follows:
public DoublyLinkedList()
A basic constructor.
public String toString()
A basic toString(), both forward and reverse.
It should return a string with two lines: The list both forward and backward. For example:
List Content Forwards: A –> B –> C
List Content Backwards: C –> B –> A
private void addAfter(DoubleListNode<ListType> prevNode, ListType value)
Adds a node containing value after the specified position in the list. (The new node should be after prevNode in the list)
This method must run in constant time, O(1), or you will lose points!
Parameters:
prevNode
— The node that the new item should be added after. If null,
then that means you are adding to an empty list.
value
— The value to add to the list
private void addBefore(DoubleListNode<ListType> postNode, ListType value)
Adds a node containing value before the specified position in the list. (The new node should be before postNode in the list)
This method must run in constant time, O(1), or you will lose points!
Parameters:
postNode
— The node that the new item should be added before. If null,
then that means you are adding to an empty list.
value
— The value to add to the list
public void addHead(ListType value)
Add an item to the head of the list.
This method must run in constant time, O(1), or you will lose points!
value
— The value to add to the list
public void add(ListType value)
Add an item to the end of the list.
This method must run in constant time, O(1), or you will lose points!
value
— The value to be added to the list
public int size()
Determine how many items are in the list
public boolean isEmpty()
Determine whether or not the list is empty.
private DoubleListNode<ListType> nodeContaining(ListType value)
Helper Function: Get the node of the first instance of a particular value in the list.
Throws a NoSuchElementException (with no extra message) if the node is not in the list.
Parameters: value
— The value to search for
Returns: The node of the first instance of value in the list
public boolean contains(ListType value)
Determine if a specified value is in the list.
Parameters: value
— The value to search for
Returns: true if the item is in the list and false otherwise
private DoubleListNode<ListType> nodeAtIndex(int index)
Helper Function: Get the node at the position in the list specified by index.
Throws an IndexOutOfBoundsException with an appropriate message if index is out of range.
Parameters: index
— The index of the item whose node is wanted
Returns: The node at the provided index
public ListType get(int index)
Get an item at a specified index from the list.
Throws an IndexOutOfBoundsException with an appropriate message if index is out of range.
Parameters: index
— The numeric index (starting from 0) of the item to get
Returns: The item
public void add(int index, ListType value)
Adds a node containing value at the specified position in the list, if such a position can be added to the list.
Throws an IndexOutOfBoundsException with an appropriate message if index is out of range.
Parameters:
index
— The index to add the new value at
value
— The value to add to the list
public ListType remove(DoubleListNode<ListType> node)
Remove the specified node from the list. You may assume the node is in the list.
This method must run in constant time, O(1), or you will lose points!
Parameters: node
— The node to remove from the list
Returns: The item contained in this node
public void remove(ListType value)
Remove the first instance of an item with a given value from the list.
Throws a NoSuchElementException (with no extra message) if the node is not in the list.
value
— The value of the item to remove.
public ListType remove(int index)
Remove the node at the position in the list specified by index, if such a position is in the list, and returns the data value that was at that location.
Throws an IndexOutOfBoundsException with an appropriate message if index is out of range.
Parameters: index
— The index of the item to remove
Returns: The item that was removed
public void removeLast(ListType target)
Removes the node containing the last occurrence of a value in the list.
Throws a NoSuchElementException (with no extra message) if the node is not in the list.
Note: You need to be intelligent here while searching. If you don’t search correctly, then you might not pass the autograder, even if your method works.
target
— The item to remove from the list
public SinglyLinkedList<ListType> asSinglyLinkedList()
Return a copy of this list transformed into a singly-linked list.
This method must run in linear time, O(N), or you will lose points.
Some of the methods listed above have efficiency requirements. The Autograder will test the efficiency of those methods to ensure your implementation meets the requirements.
To test your complexity, we time how long the method takes to run on a list with 50,000 elements. Next, we time and run it again with a list twice that size. We then divide the two times to get your speed ratio.
If your method is \(O(N)\) then your speed ratio should be \(\approx 2\). (Because if we double the input, the time to run should double as well.)
If your method is \(O(1)\) then your speed ratio should be \(\approx 1\). (Because if we double the input, the time to run should not change.)
This method of determining your speed ratio is not an exact science, and your speed ratio will vary a little bit between two runs, even if your code hasn’t changed. It should not change drastically, however.
If your code is much slower than it should be, such as \(O(N^2)\), then the time to complete an operation on a 50,000-item list will be very high. As such, the testcase will simply timeout after 4 seconds and you’ll see a message indicating that a timeout occurred. This means your solution is not efficient enough.
There are multiple parts of the grading of this assignment:
For the first 90 points, your submission will be auto-graded on Gradescope.
For the next 5 points, your submission will be manually graded to check for good implementation methodologies. (Did you use a good approach to solving the problems?)
For the next 5 points, your submission will be manually graded to check for good testcases that you include in the main method. (Do you have 2-3 of your own testcases for each method, and do they all execute automatically?)
Your code will also be checked for style. The parts of style that can be checked automatically (things like spacing, indentation, the use of CamelCase, etc.) are automatically checked by the autograder. Other parts of style, such as choosing good variable names, will be checked manually. Autograded style guide violations can result in, at most, -10 points. Manually checked style guide violations can result in, at most, -5 points.
You will submit your program to Gradescope. Log in to the system and you will see the homework. Once there, you need to submit a zip
file containing your code. Lucky for you, however, Eclipse can create this zip file for you. Check out these instructions for exporting. On Gradescope, you’ll submit that exported zip
file. On the page that follows your submission, you will see your live score (out of 90). If you receive a lower score than you expect, you can click on the score to see the feedback from the autograder.
You need to write at least two testcases for each of the methods listed above. (This is a lot of test cases for you to write.) One of those testcases should test basic functionality, and the other should test an error condition of some sort. All of your testcases should be in the LinkedListTester
class included with the skeleton code. You must follow the model of our testcases from previous assignments (meaning you print when you start, print the results (pass/fail) when you finish, etc.) Additionally, you must comment each testcase with a note describing what it tests.
You may only use the following Java libraries in this assignment:
java.util.NoSuchElementException
If you have questions, please post to Discord. The course staff, including the instructor, monitor and answer questions there.
When posting to Discord, if you include any code make sure your question is posted privately to the course staff, and not to the entire class.
Do not change the names of any of the provided methods or files. You may, however, add additional methods as needed.
After you submit to Gradescope, make sure you check your score. If you aren’t sure how to do this, then ask.
There is no partial credit on Gradescope testcases. Your Gradescope score is your Gradescope score.
Read the last bullet point again. Seriously, we won’t go back later and increase your Gradescope score for any reason. Even if you worked really hard and it was only a minor error…
You can submit to Gradescope multiple times. So, after you submit you should check your score. If it isn’t a 90 you can keep working until it is.
Don’t forget to make sure your code conforms to the style guide. We’ll be taking a look at that!
The style guide has a bunch of small rules with regard to indentation and spacing that are hard to follow perfectly. Luckily, Eclipse can handle it for you! Go to Source -> Format
in Eclipse and watch all of your spacing and indentation problems get fixed automatically. (You should do this before every submission.)