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:

  • Overview
  • A Doubly-Linked List Embedded in an Array
  • Your Task
  • Files you will need
  • Files to Submit
  • Drivers
  • Grading Standards
  • Our Suggestions
  • Extra Credit

  • Overview

    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.


    A Doubly-Linked List Embedded in an Array

    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 -23 6 9 4 9 15 17 12 0 0 0 3 -57 -1 6 589 3 -1

    By convention, assume that the three cells used to represent a node store, respectively, the Element, the reference to the Previous node, and the reference to the Next node. 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 Task

    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.


    Files you will Need

    The files you need for this assignment can be downloaded here.


    Files to Submit

    You should submit three files:


    Drivers

    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.


    Grading Standards

    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.


    Our Suggestions

    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.

  • The book chose to introduce singly-linked lists in Ch. 4.3 implemented using a variable head which references the first true element of the list. When introducing doubly-linked lists in Ch. 4.4.2, they introduced the use of "sentinels" as a programming technique. They create two additional node objects which do not actually store any element provided by the user. These "dummy" nodes are referenced as "header" and "trailer" and are used as bookends to delimit the remaining list. Either type of list can be implemented with or without sentinels, however we strongly recommend that you use these sentinels. The main advantage is to remove the need for many of the special cases (and potential bugs), such as when inserting or deleting an item from an end of the list. The only disadvantage is the space used for the two additional node structures. However the potential simplicity of your code is certainly worth the extra space, and your program will generally run faster with sentinels, because there may be one or two less instructions to execute in many of the methods.

    A word of warning: if using sentinels, please make sure that you do not count those nodes when assessing the size() or capacity of the Vector. Remember, the size and capacity are based on the point of view of the user, who, by principles of encapsulation, should not know or care about the implementation choices you made.

  • If you think about the underlying array in its rawest form, it is quite easy to introduce inadvertent bugs by using an errant index into the array. A much easier technique is to have most or all of the array indexing done from a few carefully defined utility functions. Our template provides you with several such function such as getNext() or setNext() which will allow you to write much of your code with the appearance of "standard" linked lists and nodes, even though the underlying data structure is an array.

  • Extra Credit (1 point)

    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


    Last modified: 11 October 2002