// Stuart Reges handout #10
// 5/23/05
//
// DoublyLinkedIntList provides an implementation of the IntList interface
// backed by a doubly-linked list. This will provide O(1) performance for
// adding at the front or back of the list and removing values while
// iterating over the list. Methods like get and set will be O(n) for
// values in the middle of the list.
public class DoublyLinkedIntList {
private ListNode front; // first value in the list
private ListNode back; // last value in the list
private int size; // current number of elements
// post: constructs an empty list
public DoublyLinkedIntList() {
clear();
}
// post: returns the current number of elements in the list
public int size() {
return size;
}
// pre : 0 <= index < size()
// post: returns the integer at the given index in the list
public int get(int index) {
checkIndex(index);
ListNode current = gotoIndex(index);
return current.data;
}
// post: appends the given value to the end of the list
public void add(int value) {
if (front == null) {
front = new ListNode(value);
back = front;
} else {
back.next = new ListNode(value, null, back);
back = back.next;
}
size++;
}
// pre: 0 <= index <= size()
// post: inserts the given value at the given index, shifting subsequent
// values right
public void add(int index, int value) {
if (index < 0 || index > size)
throw new IndexOutOfBoundsException("illegal index");
if (index == 0)
addFirst(value);
else if (index == size)
add(value);
else {
ListNode current = gotoIndex(index - 1);
ListNode newNode = new ListNode(value, current.next, current);
current.next = newNode;
newNode.next.prev = newNode;
size++;
}
}
// add value as new first element of the list
private void addFirst(int value) {
front = new ListNode(value, front, null);
if (front.next != null)
front.next.prev = front;
if (back == null)
back = front;
size++;
}
// pre : 0 <= index < size()
// post: removes value at the given index, shifting subsequent values left
public void remove(int index) {
checkIndex(index);
if (index == 0)
removeFirst();
else if (index == size - 1)
removeLast();
else {
ListNode current = gotoIndex(index - 1);
current.next = current.next.next;
current.next.prev = current;
size--;
}
}
private void removeFirst() {
front = front.next;
if (front == null)
back = null;
else
front.prev = null;
size--;
}
private void removeLast() {
back = back.prev;
if (back == null)
front = null;
else
back.next = null;
size--;
}
// pre : 0 <= index < size()
// post: replaces the integer at the given index with the given value
public void set(int index, int value) {
checkIndex(index);
ListNode current = gotoIndex(index);
current.data = value;
}
// post: list is empty
public void clear() {
front = null;
back = null;
size = 0;
}
// pre : 0 <= index < size()
// post: returns the node at a specific index. Uses the fact that the list
// is doubly-linked to start from the front or the back, whichever
// is closer.
private ListNode gotoIndex(int index) {
ListNode current;
if (index < size / 2) {
current = front;
for (int i = 0; i < index; i++)
current = current.next;
} else {
current = back;
for (int i = size - 1; i > index; i--)
current = current.prev;
}
return current;
}
// post: throws an exception if the given index is out of range
private void checkIndex(int index) {
if (index < 0 || index >= this.size)
throw new IndexOutOfBoundsException("illegal index");
}
}
// ListNode is a class for storing a single node of a linked list storing
// integer values. It has three public data fields for the data and the
// two links to the next node and the previous node in the list and has
// two constructors.
public class ListNode {
public int data; // data stored in this node
public ListNode next; // link to next node in the list
public ListNode prev; // link to previous node in the list
// post: constructs a node with given data and null links
public ListNode(int data) {
this(data, null, null);
}
// post: constructs a node with given data and given links
public ListNode(int data, ListNode next, ListNode prev) {
this.data = data;
this.next = next;
this.prev = prev;
}
}