CS 136 - Lecture 11
-
Other linked list representations (cont'd.)
-
Doubly-linked lists
Linked List Implementations (cont'd.)
Doubly-linked lists
Just as we needed SinglyLinkedListElements in order to build
SinglyLinkedLists, we need DoublyLinkedListElements here:
public class DoublyLinkedListElement {
protected Object value; // value held in elt
protected DoublyLinkedListElement nextElement; // successor elt
protected DoublyLinkedListElement previousElement; // predecessor elt
public DoublyLinkedListElement(Object v,
DoublyLinkedListElement next,
DoublyLinkedListElement previous)
// post: constructs new element with list
// prefix referenced by previous and
// suffix referenced by next
{
data = v;
nextElement = next;
if (nextElement != null)
nextElement.setPrevious(this);
previousElement = previous;
if (previousElement != null)
previousElement.setNext(this);
}
DoublyLinkedListElement(Object v)
// post: constructs a single element
{
this(v,null,null);
}
public DoublyLinkedListElement next()
// post: returns the element that follows this
{
return nextElement;
}
public DoublyLinkedListElement previous()
// post: returns element that precedes this
{
return previousElement;
}
public Object value()
// post: returns value stored here
{
return data;
}
public void setNext(DoublyLinkedListElement next)
// post: sets value associated with this element
{
nextElement = next;
}
public void setPrevious(DoublyLinkedListElement previous)
// post: establishes a new reference to a previous value
{
previousElement = previous;
}
public void setValue(Object value)
// post: sets a new value for this object
{
data = value;
}
}
With this defined, it is now easy to define a doubly-linked
list. This time we'll keep track of both the first (head) and
last (tail) elements of the list so we can get to tail
quickly.
public class DoublyLinkedList implements List {
protected DoublyLinkedListElement head;
protected DoublyLinkedListElement tail;
protected int count;
public DoublyLinkedList()
// post: constructs an empty list
{
head = null;
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 is not null
// post: adds element to head of list
{
// construct a new element, making it the head
head = new DoublyLinkedListElement(value, head, null);
// fix tail, if necessary
if (tail == null)
tail = head;
count++;
}
public Object removeFromHead()
// pre: list is not empty
// post: removes first value from list
{
Assert.pre(!isEmpty(),"List is not empty.");
DoublyLinkedListElement temp = head;
head = head.next();
if (head != null)
head.setPrevious(null);
else
tail = null; // remove final value
temp.setNext(null);// clean things up; temp is free
count--;
return temp.value();
}
public void addToTail(Object value)
// pre: value is not null
// post: adds new value to tail of list
{
// construct new element
tail = new DoublyLinkedListElement(value, null, tail);
// fix up head
if (head == null)
head = tail;
count++;
}
public Object removeFromTail()
// pre: list is not empty
// post: removes value from tail of list
{
Assert.pre(!isEmpty(),"List is not empty.");
DoublyLinkedListElement temp = tail;
tail = tail.previous();
if (tail == null)
head = null;
else
tail.setNext(null);
count--;
return temp.value();
}
public Object peek()
// pre: list is not empty
// post: returns first value in list.
{
return head.value();
}
public Object tailPeek()
// pre: list is not empty
// post: returns last value in list.
{
return tail.value();
}
public boolean contains(Object value)
// pre: value not null
// post: returns true iff value is in the list
{
DoublyLinkedListElement 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. List can be empty.
// post: first element matching value is removed from list
{
DoublyLinkedListElement finger = head;
while (finger != null && !finger.value().equals(value))
finger = finger.next();
if (finger != null)
{
// fix next field of previous element
if (finger.previous() != null)
finger.previous().setNext(finger.next());
else
head = finger.next();
// fix previous field of following element
if (finger.next() != null)
finger.next().setPrevious(finger.previous());
else
tail = finger.previous();
count--; // fewer elements
return finger.value();
} // Didn't find value
return null;
}
public int size()
// post: returns the number of elements in list
{
return count;
}
public boolean isEmpty()
// post: returns true iff the list has no elements.
{
return size() == 0;
}
public void clear()
// post: removes all the elements from the list
{
head = tail = null;
count = 0;
}
}
RemoveFromTail is now O(1), but tradeoff is now all addition and removal
operations must set one extra pointer in element. Must also worry about
changing head and tail of the list.