CS 136 - Lecture 11

  1. Other linked list representations (cont'd.)
    1. 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.