Programming Assignment
Due:
Please make sure you adhere to the policies on
academic honesty.
Please see the general programming webpage for details about
the programming environment for this course, and specifically for
directions in how to submit your programming assignment electronically.
The files you need for this assignment can be
downloaded here.
Contents:
Learning about linked lists also provides us with an opportunity to
learn a larger lesson about memory management within
programming languages. One of the benefits of using a linked list
data structure, versus an array-based data structure, is that there is
no a priori capacity constraint placed on the data structure. Of
course, there really is some eventual limit on available memory for
any true computer system (though perhaps it is quite large). Thus, a
linked list does not truly provide opportunity to use unbounded memory.
In reality, memory for a system can be viewed as one large,
consecutive array of memory cells. When programming in Java, or most
other high-level languages, the systems takes care of most of the
details of managing this memory. For example, when you, as a
programmer, want memory for creating an additional node in a list, you
might give an instruction such as,
Node extra = new Node();
When interpreting this instruction, Java finds available cells of memory
which can be used by you for an object of class Node. Our goal for
this programming assignment is to have you write code for maintaining
a doubly-linked list directly within an array of memory cells, rather
than relying on Java's memory management.
Conceptually, doubly-linked lists are introduced (for another purpose)
in Ch. 4.4.2 of the text. However, as discussed in the
overview, we want you to implement a doubly-linked list by directly
using an underlying array for memory. For singly-linked lists,
this method was demonstrated by an
Applet in which each "node" of the list was represented by two
cells of the array, the "element" and the "next" reference. The same
idea can be used to represent a doubly-linked list, however using
three consecutive array cells for each "node" of the list. As an
example, consider the following memory contents:
| Array Index |
0 |
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
| Array Contents |
6 |
-23 |
9 |
9 |
4 |
15 |
12 |
17 |
0 |
0 |
0 |
3 |
-1 |
-57 |
6 |
3 |
589 |
-1 |
By convention, assume that the three cells used to represent a node
store, respectively, a Previous reference, an Element, and a Next
reference. For the above example, if the header sentinel is stored in
the node starting at index 12 and trailer sentinel is stored in the
node starting at index 15, then we claim that the preceding array
represents a linked list with contents:
header <-->
17 <-->
-23 <-->
0 <-->
4 <-->
trailer
Notice that a legitimate reference in this list must be a non-negative
number, and so negative numbers can be used to represent "null"
pointers. Elements, however, might be any integer as they are
provided by the user. Finally, since the header and trailer are
sentinels and do not represent true elements of the list, the
"element" field of those nodes is irrelevant
Your goal for this assignment is to provide an implementation for the
Vector ADT, as defined in Ch. 5.1 of the text (though
for simplicity, this assignment uses a modified version of this
interface, where elements are of type int rather than
Object). For those interested, we provide
basic documentation for the methods of the given VectorADT
interface, as used in this assignment.
The rest of Ch 5.1 discusses a "simple array-based
implementation" in Ch 5.1.2, and the extendible array
implementation, such as with java.util.Vector, in
Ch 5.1.3-5.1.4. We do not want you to use either of those
implementations. We want you to implement the Vector as a
doubly-linked list embedded in an array.
Using a linked-list to implement the VectorADT is relatively
straightforward. One drawback, however, is that most of the Vector
methods are based on the concept of the "rank" of an element.
Therefore, if trying to find elemAtRank(145), you will need
to perform a traversal of the linked list, from the header, to find
the 146th true element of the list (yes, we really meant to
say 146th; in fact it will be in the 147th
node). If you want to be slightly more clever in some cases, you
might choose to walk backwards from the end of the list, though you
are not required to do this.
The additional challenge is that you will have to perform your own
memory management. When the insertAtRank method is called, a
block of three available cells must be found somewhere in the
underlying array. The key will be for you to differentiate between
those cells which are in use and those cells which are available. You
can accomplish this without any additional variables by marking the
unused cells by setting the "next" and "prev" references to be "null"
(recall that true references will never be negative, so negative
numbers can be used to represent a "null" reference). By carefully
using such a convention, the insertAtRank method can scan
through all possible blocks, looking for one which is currently
unused.
Finally, please note that the constructor for your LinkedVector
implementation will be given a parameter which is to be the maximum
"capacity" of your Vector, measured in terms of the number of
elements it will hold for the user. Please keep in mind that this
is different from the length of the array, as three array cells will
be needed for each doubly-linked node. Also, please note that if you
choose to use sentinel nodes, as in Ch. 4.4.2, those nodes should
not be counted against the specified capacity. If it is ever the
case that the Vector's size() is equal to the given capacity
and yet the user calls insertAtRank, your implementation
should throw a VectorFullException.
The files you need for this assignment can be
downloaded here.
You should submit three files:
- LinkedVector.java - which contains your implementation
of the VectorADT interface.
- readme - a brief summary of your program, and any
further comments you wish to make to the grader.
- inputfile - you should provide a sample input file
which we will use in testing all of the other students' submissions. The
file format for inputfiles is based on use with the
text-based driver
we provide below. Your input file may use at most 100 commands
In order to make sure that your inputfile follows the proper format, we
strongly recommend that you run your program on your inputfile to make
sure it is accepted by the driver.
We will provide you with two different drivers which you may find very
helpful in testing your implementations. One driver is menu-driven,
using text input from either the keyboard or a file. The other driver
is an "Applet" which has a graphical user interface.
- MemoryDriver.java
This is a text-based, menu-driven program for testing your vector
implementation. The menu allows you to call any combination of
VectorADT methods. You may even call the constructor anytime you want
to restart with a different capacity.
By default, the program takes input from the keyboard. However the
driver has an optional feature which allows it to read input from a
file rather than from a keyboard. The file should have the identical
characters which you would use if you were typing them yourself on the
keyboard. This feature simply allows you to test and retest your
program on a particular input without having to retype the input each
time you want to run the program. It is also convenient at times when
running with a debugger to have the input read from a file rather than
from the keyboard. If the program reaches the end of the file without
exiting, then it will revert back to reading input from the keyboard
for the remainder of the program.
To use this feature, you must specify the exact filename (including
any suffix), as a single argument at runtime. Details on how to
provide such arguments are given in the general
programming webpage.
- MemoryApplet.java
This program allows you to test your implementations from a graphical
interface that allows you to call methods by the click of a button.
We have included an html file MemoryApplet.html which can
be used in viewing the applet. Further instructions for running an
applet can be found in the general programming
webpage.
The assignment is worth 10 points. Eight points will be
awarded based on our own evaluation of your assignment and the readme
file. One additional point will be awarded fractionally based on how
well your program performs on other students' test inputs. The final
point will be awarded fractionally based on how well your test input
fools other students' flawed programs.
All strict requirements for your assignment have already been
specified above. In this section, we wish to make several suggestions
to point you towards a successful start. Many of our suggestions are
matched by the template file, LinkedVector.java, which we provide.
You are free to ignore any or all of these suggestions, if you are
confident in your own design.
We want you to revisit the issue of memory management, and
specifically how you locate an "empty" node when one is needed.
In the standard version of this assignment, we allowed you to scan
through all possible blocks looking for an empty one. That technique
is unnecessarily inefficient, as it may require linear time in the
worst case.
A better approach is to keep track of all the currently empty nodes by
linking them together (using the existing cells)! This
linked list of empty nodes will be independent of the linked list used
for the current Vector. By linking up all of the empty cells, you
will always be able to find an available cell in O(1) time, presuming
the capacity is not exhausted.
Note: Please do not let your extra credit attempt interfere
with the correctness of the rest of your program. If you are
confident that you have done the extra credit correctly, you
may submit that code. However, if there are unexpected errors, you
risk losing points from the main part of the assignment.
Alternatively, you may submit two different versions of your