First provide SinglyLinkedListElement class representing the nodes:
public class SinglyLinkedListElement {
protected Object data; // value stored in this element
protected SinglyLinkedListElement nextElement;
// ref to next element
// constructors
public SinglyLinkedListElement(Object v, SinglyLinkedListElement next)
// post: constructs a new element with value v,
// followed by next
{
data = v;
nextElement= next;
}
public SinglyLinkedListElement(Object v)
// post: constructs a new element of a list with value v
// but with nothing attached.
{
this(v,null);
}
public SinglyLinkedListElement next()
// post: returns reference to next value in list
{
return nextElement;
}
public void setNext(SinglyLinkedListElement next)
// post: sets reference to new next value
{
nextElement = next;
}
public Object value()
// post: returns value associated with this element
{
return data;
}
public void setValue(Object value)
// post: sets value associated with this element
{
data = value;
}
public String toString()
// post: returns string representation of element
{
return "<SinglyLinkedListElement: "+value()+">";
}
}
public class SinglyLinkedList implements List {
protected SinglyLinkedListElement head; // first elt
protected int count; // list size
public SinglyLinkedList()
// post: generates an empty list.
{
head = null;
count = 0;
}
public void add(Object value)
// post: adds value to beginning of list.
{
addToHead(value);
}
public void addToHead(Object value)
// post: add value to beginning of list.
{
// note the order that things happen:
// head is parameter, then assigned!!!
head = new SinglyLinkedListElement(value, head);
count++;
}
public Object removeFromHead()
// pre: list is not empty
// post: removes and returns value from beginning of list
{
SinglyLinkedListElement temp = head;
head = head.next(); // move head down the list
count--;
return temp.value();
}
public void addToTail(Object value)
// post: adds value to end of list
{
// location for the new value
SinglyLinkedListElement temp =
new SinglyLinkedListElement(value,null);
if (head != null)
{
// pointer to possible tail
SinglyLinkedListElement finger = head;
while (finger.next() != null)
finger = finger.next();
finger.setNext(temp);
}
else
head = temp;
count++;
}
public Object removeFromTail()
// pre: list is not empty
// post: last value in list is returned
{
// keep two ptrs w/ previous one elt behind finger
SinglyLinkedListElement finger = head;
SinglyLinkedListElement previous = null;
Assert.pre(head != null,"List is empty!");
while (finger.next() != null) // find end of list
{
previous = finger;
finger = finger.next();
}
// finger is null, or points to end of list
if (previous == null) // list had 1 element
head = null;
else // pointer to last element reset to null.
previous.setNext(null);
}
count--;
return finger.value();
}
public Object peek()
...
public Object tailPeek()
// find end of list as in removeFromTail
public boolean contains(Object value)
// pre: value is not null
// post: returns true iff value is found in list.
{
SinglyLinkedListElement finger = head;
while (finger != null && !finger.value().equals(value))
finger = finger.next();
return finger != null;
}
public Object remove(Object value)
// pre: value is not null
// post: removes 1st element with matching value, if any.
{
SinglyLinkedListElement finger = head;
SinglyLinkedListElement previous = null;
while (finger != null && !finger.value().equals(value))
{
previous = finger;
finger = finger.next();
}
// finger points to target value
if (finger != null) {
// we found the element to remove
if (previous == null) // it is first
head = finger.next();
else // it's not first
previous.setNext(finger.next());
count--;
return finger.value();
}
// didn't find it, return null
return null;
}
public int size()
// post: returns the number of elements in list
{
return count;
}
public boolean isEmpty()
// post: returns true iff the list is empty
{
return size() == 0;
}
public void clear()
// post: removes all elements from the list
{
head = null;
count = 0;
}
}
Most common errors in working with linked structures are ignoring these cases! (Recall the CircularVector class you implemented and the special cases you needed to handle when removing elements.)
size(), isEmpty(), peek() // O(1) in both
tailPeek(),
removeFromTail() // O(1) in Vector, O(n) in Linked
clear()
addToHead(Object value)
removeFromHead() // O(n) in Vector, O(1) in Linked
contains(Object value)
remove(Object value) // O(n) in both
addToTail(Object value) // O(n) in Linked,
// varies in Vector - usually O(1)
If list of size n kept in vector, then if "tight fit" (underlying array has exactly n elements), then need n*words(value), where words(value) is the amount of space necessary to hold a value stored in the list (including the initial reference). But underlying array may be much, much larger (remember underlying array never shrinks), so may use much more space.
Linked list representation is more predictable: space for count & head, and then for each node, space for value plus one reference.
Linked always O(n), Vector usually so.
Note. If we didn't keep count field, size operation would become O(n) in Linked list, but would save time to update count in remove and add operations.
public class CircularList implements List {
protected SinglyLinkedListElement tail;
protected int count;
public CircularList()
// pre: constructs a new circular list
{
tail = null;
count = 0;
}
public void add(Object value)
// post: adds value to beginning of list.
{
addToHead(value);
}
public void addToHead(Object value)
// pre: value non-null
// post: adds element to head of list
{
SinglyLinkedListElement temp =
new SinglyLinkedListElement(value);
if (tail == null) {
tail = temp;
tail.setNext(tail);
}
else {
temp.setNext(tail.next());
tail.setNext(temp);
}
count++;
}
public void addToTail(Object value)
// pre: value non-null
// post: adds element to tail of list
{
addToHead(value);
tail = tail.next(); // moves new from head to tail
}
public Object peek()
// pre: !isEmpty()
// post: returns value at head of list
{
return tail.next().value();
}
public Object tailPeek()
// pre: !isEmpty()
// post: returns value at tail of list
{
return tail.value();
}
public Object removeFromHead()
// pre: !isEmpty()
// post: returns and removes value from head of list
{
SinglyLinkedListElement temp = tail.next(); // ie. the head of the list
if (tail == tail.next()) // 1 elt in list
tail = null;
else {
tail.setNext(temp.next());
temp.setNext(null); // helps clean things up
} // temp is free
count--;
return temp.value();
}
public Object removeFromTail()
// pre: !isEmpty()
// post: returns and removes value from tail of list
{
Assert.pre(!isEmpty(),"The list is not empty.");
SinglyLinkedListElement finger = tail;
while (finger.next() != tail)
finger = finger.next();
// finger now points to second-to-last value
SinglyLinkedListElement temp = tail;
if (finger == tail)
tail = null;
else {
finger.setNext(tail.next());
tail = finger;
}
count--;
return temp.value();
}
public boolean contains(Object value)
// pre: value != null
// post: returns true if list contains value, else false
{
if (tail == null) return false;
SinglyLinkedListElement finger;
finger = tail.next();
while ((finger != tail) && (!finger.value().equals(value)))
finger = finger.next();
return finger.value().equals(value);
}
public Object remove(Object value)
// pre: value != null
// post: remove & returns element equal to value, or null
{
if (tail == null) return null;
SinglyLinkedListElement finger = tail.next();
SinglyLinkedListElement previous = tail;
int compares;
for (compares = 0;
(compares < count) && (!finger.value().equals(value));
compares++)
{
previous = finger;
finger = finger.next();
}
if (finger.value().equals(value)) {
// an example of the pigeon-hole principle
if (tail == tail.next())
tail = null;
else {
if (finger == tail)
tail = previous;
previous.setNext(previous.next().next());
}
// finger value free
finger.setNext(null) // to keep things disconnected
count--; // fewer elements
return finger.value();
}
else
return null;
}
public int size()
// post: returns number of elements in list
{
return count;
}
public boolean isEmpty()
// post: returns true if no elements in list
{
return tail == null;
}
public void clear()
// post: removes all elements from list.
{
count = 0;
tail = null;
}
}