CISC 3130 — Lab #8

CISC 3130
Data Structures
Lab #8
Linked Representations

How to Develop and Submit your Labs

Make sure you read the sections in the above on how to interpret CodeLab string comparison feedback

Overview

This lab has you implementing the removeLast method for the various linked list variations: singly-linked, doubly-linked; one-pointer, two-pointer; linear, circular; with and without header. The implementations will be prefixed by their flavor: link-type topology #-pointers uses-header; e.g, SL1N — Single-linked, Linear, 1-pointer, No Header

Aside from the programming with nodes and linked representations, the objective is to have you get a feeling for the 'appropriateness' of an implementation wrt a particular operation, e.g. is a single-linked, linear, single-pointer implementation a good choice when implementing an addLast method.

Supplied Code

In order to allow you to work on these in your IDE prior to submitting them to CodeLab, I've provided all the necessary code, requiring you to only have to write removeLast: The interface, canonical demo, and collection classes are provided as both .class and .java files; I suggest you incoprprate the .class files into your IDE's project for the experience, but you are of course free to use the .java file if you prefer. The skeletal implementations are provided as .java files only as they are incomplete.

All the above files can be access via the Code link at the bottom of this page/

Choice of Container and Operation

Why Deque?

Deque is the second most general of the sequences (List/Sequence being the most general). While List's arbitrary position-based add/remove would be more challenging, I wanted something a bit more accessible. Deque's add and remove operations appear symmetrical, but are not, yet they do share common objectives. Providing three of them and having you code the fourth seemed a reasonable option.

Why removeLast?

So while I was 'merciful' with respect to the choice of container (Deque vs List), I wanted the most interesting Deque operation, i.e. the one that would be the most sensitive to changes in the implementation of the linked list. removeLat fits that bill: adFirst and removeFirst operate at the front of the list eliminating any need to circularity of tail pointers. While addLast does require access to the end of the list, removeLast goes a step farther and requires access to the next-to-last node as well.

Linked List Flavors

The following sections compare contrasting attributes for the lined list, specifying the advantages and disadvantages of each, particularly with respect to the implementation of the operations of a deque.

Singly-linked vs Doubly-linked

Linear vs Circular

Single-pointer vs Head-and-Tail Pointers

Header Nodes

Leveraging Methods

As mentioned above, you have all the code for the implementations, except for removeLast. You are thus provided with quite a bit of logic — some of it similar to that used in removeLast.

'Leveraging' off size and toString

Several of the implementations require a traversal to get to the end of the deque to perform a removeLast. You can find similar logic in the size and toString methods.

Leveraging removeFirst

When the array has a single element, not only is it the last, it is also the first, and thus you can call the already written removeFirst from , i.e., leveraging it. Often much of the special logic involved in going from a non-empty deque to an empty one can be avoided by having removeLast invoke removeFirst in that situation. You can see similar logic in the addLast code (i.e., leveraging addFirst).

OTOH, if you're interested in getting into the weeds, you might consider avoiding invoking removeFirst from removeLast, it forces you to deal with any special case logic on your own, and may give you greater insight into the advantages/disadvantages of the various implementations.

The Supporting Code

Aside from the actual implementation, you are provided with several other source/class files:

The Implementations

Given four binary attributes — number of links, linear/circular, number of external pointers, header or not — there are 16 flavors of implementations. Some of them require traversals, others have somewhat involved logic (i.e., special cases), and others don't provide any benefit in their implementation. We discuss each of these issues in the particular implementations.

Each of the following sections contains a link to the implementation's code directory containing the skeletal Deque class source for the particular implementation. This code is missing the removeList metho, which you are to supply. The directory also contains the implementation's demo app.

The useless implementations are also listed but have not links.

Suggested Order(s) of Implementation

The ideal order is to start with 8.01 and proceed in order through 8.11. This will give you an opportunity to see how the various attributes of multiple pointers, circularity, and header affect the implementation.

OTOH, if you are feeling a bit unsure of yourself or are intimidated by manipulating references as 'pointers', you might consider doing 8.11 (DC1H) first — it's actually the simplest as you can read in it's notes section. You might then try 8.01 (SL1N) and then 8.02 (SL1H); the former to get you going with the traversal, and the latter to warm you up to the header node.

Either way, if you manage to complete them all, good job! You should be fairly fluent in woring with linked representations by then.

The Labs

Code the removeLast method for each of the following deque implementations. I would recommend you copy the related code into your IDE, add the method, and get it working (i.e., produce the output as shown in the "The Supporting code" section above), before submitting it to CodeLab (see last section below).

Lab 8.01 Singly-Linked, Linear, One Pointer, No Header (SL1N)

Write the removeLast method for a Deque class implemented using a singly-linked, linear, one pointer, no header node list

  • Please supply the method only, not the entire class. The first node on the list is referenced by the instance variable head; head should be null when the list is empty.
  • The other methods of the class have already been implemented; feel free to use them. The interface for Deque can be found above.
Notes
  • If you call removeFirst from removeList, there's no special logic
    • the singleton case (i.e., only one node on the list) is handled by removeFirst
    • if you don't use removeFirst, you need to make sure head is set to null when you remove the only remaining node on the list
  • This implementation requires a traversal to the rear

Lab 8.02 Singly-Linked, Linear, One Pointer, Header (SL1H)

Write the removeLast method for a Deque class implemented using a singly-linked, linear, one pointer, with header node list

  • Please supply the method only, not the entire class. The header node is referenced by the instance variable header
  • The other methods of the class have already been implemented; feel free to use them. The interface for Deque can be found above.
  • There's no special logic in this implementation regardless of whether you call removeFirst from removeLast for the singleton case:
    • that's the whole point of using a header node; to eliminate special logic when going from empty-to-singleton or vice-versa
  • This implementation requires a traversal to the rear

Lab 8.03 Singly-Linked, Linear, Two Pointers, No Header (SL2N)

Write the removeLast method for a Deque class implemented using a singly-linked, linear, two pointers (head and tail)

  • Please supply the method only, not the entire class. The first node is referenced by an instance variable head, and the last node is referenced by the instance variable tail. Both references are null when the list is empty.
  • The other methods of the class have already been implemented; feel free to use them. The interface for Deque can be found above.
  • The challenge in this implementation is keeping the two pointers in synch, i.e., making sure that when one goes to or from null the other one does as well.
    • Again, using removeFirst avoids this whole issue.
  • Notice a traversal is still required for removal from the rear.

Lab 8.04 Singly-Linked, Linear, Two Pointers, Header (SL2H)

Write the removeLast method for a Deque class implemented using a singly-linked, linear, two pointers (head and tail), with header node list

  • Please supply the method only, not the entire class. The header node is referenced by an instance variable header, and the last node is referenced by the instance variable tail. tail should be null when the list is empty.
  • The other methods of the class have already been implemented; feel free to use them. The interface for Deque can be found above.
  • Not sure the header helps much in this implementation
  • There's no need nor use for a trailer node; as you can see in the code, is initialized to null (and thus is null for an empty list), and references the last node in a non-empty list. This is one of the more annoying implementations: you have to maintain a header node AND two pointers with essentially no benefit from either.

Lab 8.05 Singly-Linked, Circular, One Pointer, No Header (SC1N)

Write the removeLast method for a Deque class implemented using a singly-linked, circular, one pointer (tail), with no header node list.

  • Please supply the method only, not the entire class. The last node on the list is referenced by an instance variable tail. tail should be null when the list is empty.
  • The other methods of the class have already been implemented; feel free to use them. The interface for Deque can be found above.
  • Making the list circular in structure allows a single pointer (tail), providing convenient access to the last and first nodes on the list
  • Having the single pointer removes the synch issue.
  • There still is an issue of the transition from empty-to-singleton, and vice-versa, with respect to setting tail from/to null; again calling removeFirst from removeLast handles that situation.
  • As with SL2N, a traversal is still required for removal from the rear.
  • This implementation, together with DC1N are two of the rare situations where a do … while loop comes in handy

Singly-Linked, Circular, One Pointer, Header (SC1H)

This implementation provides no benefits.

  • As this is a singly linked list so we can't move backwards. Placing a header puts it between the last and first nodes on the list proper. Having a tail pointer then pointing at the header does indeed give easy access to the first node (its the one after the header), but getting to the last node requires a traversal, since we can't move back from the tail. As such the header proves to be of no value.

Singly-Linked, Circular, Two Pointers, No Header (SC2N)

This implementation provides no benefits.
  • There is no need for a second pointer once a circular structure is used … a tail pointer is sufficient in that is points to the last element and its successor is thus the first element.

Singly-Linked, Circular, Two Pointers, Header (SC2H)

This implementation provides no benefits.
  • There is no need for a second pointer once a circular structure is used … a tail pointer is sufficient in that is points to the last element and its successor is thus the first element.

Lab 8.06 Doubly-Linked, Linear, One Pointer, No Header (DL1N)

Write the removeLast method for a Deque class implemented using a doubly-linked, linear, one pointer, no header node list

  • Please supply the method only, not the entire class. The first node on the list is referenced by an instance variable head. head is null when the list is empty.
  • The other methods of the class have already been implemented; feel free to use them. The interface for Deque can be found above.
  • Not much different than SL1N other than having to take the prev field into consideration
  • As with the other linear, single pointer implementations, a traversal is still required for operations at the rear.

Lab 8.07 Doubly-Linked, Linear, One Pointer, Header (DL1H)

Write the removeLast method for a Deque class implemented using a doubly-linked, linear, one pointer, with header node list

  • Please supply the method only, not the entire class. The header node is referenced by an instance variable header.
  • The other methods of the class have already been implemented; feel free to use them. The interface for Deque can be found above.
  • Not much different than SL1H other than having to take the prev field into consideration
  • As with the other linear, single pointer implementations, a traversal is still required for operations at the rear.

Lab 8.08 Doubly-Linked, Linear, Two Pointers, No Header (DL2N)

Write the removeLast method for a Deque class implemented using a doubly-linked, linear, two pointers, no header node list

  • Please supply the method only, not the entire class. The first node is referenced by an instance variable head, and the last node is referenced by the instance variable tail. Both references are null when the list is empty.
  • The other methods of the class have already been implemented; feel free to use them. The interface for Deque can be found above.
  • With two pointers and double links, no traversals are necessary
  • having the first and last nodes of the list contain null in their prev and next fields respectively requires special logic for the empty-to-singleton (and vice-versa) case.
    • this can be avoided by calling removeFirst

Lab 8.09 Doubly-Linked, Linear, Two Pointers, Header (DL2H)

Write the removeLast method for a Deque class implemented using a doubly-linked, linear, two pointers, with header node list

  • Please supply the method only, not the entire class. The header node is referenced by an instance variable header, and the last node is referenced by the instance variable tail. tail is null when the list is empty.
  • The other methods of the class have already been implemented; feel free to use them. The interface for Deque can be found above.
  • With two pointers and double links, no traversals are necessary
  • There is still a bit of special logic for the empty-to-singleton case (and vice-versa)
    • This is to set tail to/from null
    • There is no special logic for the prev of the first node, since there's a header.
    • not sure if a trailer node would solve this

Lab 8.10 Doubly-Linked, Circular, One Pointer, No Header (DC1N)

Write the removeLast method for a Deque class implemented using a doubly-linked, circular, one pointer (head), no header node list

  • Please supply the method only, not the entire class. The first node of the list is referenced by an instance variable head. head. head is null when the list is empty.
  • The other methods of the class have already been implemented; feel free to use them. The interface for Deque can be found above.
  • With a circular structure and double links, no traversals are necessary
  • The external reference could have just as easily been the tail
  • The special logic is setting head to/from null in the empty-to-singleton cases.
    • Again, this can be avoided in removeLast by calling removeFirst
    • The circular structure eliminates the need for any special logic to handle null at either end.
  • This implementation, together with SC1N are two of the rare situations where a do … while loop comes in handy

Lab 8.11 Doubly-Linked, Circular, One Pointer, Header (DC1H)

Write the removeLast method for a Deque class implemented using a doubly-linked, circular, one pointer, with header node list

  • Please supply the method only, not the entire class. The header node is referenced by an instance variable header. head.
  • The other methods of the class have already been implemented; feel free to use them. The interface for Deque can be found above.
  • With a circular structure and double links, no traversals are necessary
  • There is no special logic in this implementation
    • The circularity eliminates null at either end
    • The header eliminates the transition from (physically) empty to non-empty and vice-versa
    • The single pointer eliminates any need to synchronize two pointers

Doubly-Linked, Circular, Two Pointers, No Header (DC2N)

This implementation provides no benefits.
  • There is no need for a second pointer once a circular structure is used … a tail pointer is sufficient in that is points to the last element and its successor is thus the first element.

Doubly-Linked, Circular, Two Pointers, Header DC2H)

This implementation provides no benefits.
  • There is no need for a second pointer once a circular structure is used … a tail pointer is sufficient in that is points to the last element and its successor is thus the first element.

Submitting to CodeLab

Each of the above labs has a corresponding CodeLab exercise; all you need to do is submit your method, all the rest of the code has been supplied.

Code Relevant to this Lab