Lecture 3 - Linked List
Lecture 3 - Linked List
Introduction
When studying Java's built-in array type, we were quite annoyed by the limitations of these arrays:
fixed capacity: once created, an array cannot be resized. The only way to "resize" is to create a
larger new array, copy the elements from the original array into the new one, and then change the
reference to the new one. Very inefficient, but of course we can use a bit of intelligence to make it a
bit better (eg., double the space each time instead of adding just the required amount, etc).
shifting elements in insert/remove: since we do not allow gaps in the array, inserting a new element
requires that we shift all the subsequent elements right to create a hole where the new element is
placed. In the worst case, we "disturb" every slot in the array when we insert in the beginning of the
array!
Removing may also require shifting to plug the hole left by the removed element. If the ordering of
the elements does not not matter, we can avoid shifting by replacing the removed element with the
element in the last slot (and then treating the last slot as empty).
The answer to these problems is the linked list data structure, which is a sequence of nodes connected by
links. The links allow inserting new nodes anywhere in the list or to remove an existing node from the list
without having to disturb ther rest of the list (the only nodes affected are the ones adjacent to the node
being inserted or removed). Since we can extend the list one node at a time, we can also resize a list until
we run out of resources. However, we'll find out soon enough that what we lose in the process — random
access, and space! Linked list is a sequence container that supports sequential access only, and requires
additional space for at least n references for the links.
We maintain a linked list by referring to the first node in the list, conventionally called the head reference.
All the other nodes can then be reached by traversing the list, starting from the head. An empty list is
represented by setting the head reference to the null. Given a head reference to a list, how do you count
the number of nodes in the list? You have to iterate over the nodes, starting from head, and count until
you reach the end.
As I mentioned earlier, a linked list is a sequence of nodes. To put anything (primitive type or object
reference) in the list, we must first put this "thing" in a node, and then put the node in the list. Compare
this with an array – we simply put the "thing" (our primitive type or object reference) directly into the
array slot, without the need for any extra scaffolding. Our Node class is quite simple — it contains
element (an object reference if we have a list of objects), and a reference to the next node in the list:
/**
* Creates a new node with given element and next node reference.
*
* @param e the element to put in the node
file:///Users/mumit/Sites/Classes/Summer-11/CSE-220/lectures/linked-list-notes.html Page 1 of 12
Linked lists in Java 8/11/11 1:16 PM
This allows you a create a singly-linked list, and as a result, we can only move forward in the list by
following the next links. To be able to move backward as well, we'll have to add another reference to the
previous node, increasing the space usage even more.
file:///Users/mumit/Sites/Classes/Summer-11/CSE-220/lectures/linked-list-notes.html Page 2 of 12
Linked lists in Java 8/11/11 1:16 PM
example, the following prints out each element (stored within the nodes) in the list.
Node n = head;
while (n != null) {
System.out.println(n.element);
n = n.next;
}
return count;
}
/**
* Returns the element at the given index in the given list.
*
* @param head the reference to the head node of the list
* @param index the index of the element to get
* @return the element at the given index, or null if the index is out
* of bounds.
*/
public static Object get(Node head, int index) {
if (index < 0)
return null; // invalid index.
file:///Users/mumit/Sites/Classes/Summer-11/CSE-220/lectures/linked-list-notes.html Page 3 of 12
Linked lists in Java 8/11/11 1:16 PM
int i = 0;
for (Node n = head; n != null; n = n.next) {
if (i == index)
return n.element;
else
i++;
}
return null; // index out of bounds.
}
If we know the number of elements in the list (ie., the size), then it's much easier.
/**
* Returns the element at the given index in the given list.
*
* @param head the reference to the head node of the list
* @param size the number of elements in the list
* @param index the index of the element to get
* @return the element at the given index, or null if the index is out
* of bounds.
*/
public static Object get(Node head, int size, int index) {
if (index < 0 || index >= size)
return null; // invalid index.
Node n = head;
for (int i = 0; i < index; i++, n = n.next)
;
return n.element;
}
It's typical in most applications to write a method to get the node at a given index, so that we can get the
element by first getting the node, and the return the element within. We define a nodeAt method to return
the node at the given index.
/**
* Returns the node at the given index in the given list.
*
* @param head the reference to the head node of the list
* @param size the number of elements in the list
* @param index the index of the node to get
* @return the node at the given index, or null if the index is out
* of bounds.
*/
public static Node nodeAt(Node head, int size, int index) {
if (index < 0 || index >= size)
return null; // invalid index.
Node n = head;
for (int i = 0; i < index; i++, n = n.next)
;
return n;
}
file:///Users/mumit/Sites/Classes/Summer-11/CSE-220/lectures/linked-list-notes.html Page 4 of 12
Linked lists in Java 8/11/11 1:16 PM
/**
* Returns the element at the given index in the given list.
*
* @param head the reference to the head node of the list
* @param size the number of elements in the list
* @param index the index of the element to get
* @return the element at the given index, or null if the index is out
* of bounds.
*/
public static Object get(Node head, int size, int index) {
Node node = nodeAt(head, size, index);
if (node == null)
return null; // invalid index.
else
return node.element;
}
/**
* Sets the element to the given one at the given index in the given list.
*
* @param head the reference to the head node of the list
* @param size the number of elements in the list
* @param index the index of the element to update
* @param elem the element to update the value with
* @return the old element at index if valid, or null if the index is out
* of bounds.
*/
public static Object set(Node head, int size, int index, Object elem) {
Node node = nodeAt(head, size, index);
if (node == null)
return null; // invalid index.
else {
Object oldElem = node.element;
node.element = elem;
return oldElem;
}
}
typical variants: return the index of the given element (indexOf), or return true if the element exists
(contains). Both of these require that we walk through the list and check for the existence of the given
element.
/**
* Returns the index of the element in the list.
*
* @param head the reference to the head node of the list
* @param size the number of elements in the list
* @param elem the element to find the index of
* @return the index of the element is found, or -1 otherwise
*/
public static int indexOf(Node head, int size, Object elem) {
int i = 0;
for (Node n = head; n != null; n = n.next, i++)
if (n.element.equals(elem))
return i;
The other variant returns true if the element is found, or false otherwise.
/**
* Returns true if the element exists in the list.
*
* @param head the reference to the head node of the list
* @param size the number of elements in the list
* @param elem the element to find the index of
* @return true if the element is found, or false otherwise
*/
public static boolean contains(Node head, int size, Object elem) {
int i = 0;
for (Node n = head; n != null; n = n.next, i++)
if (n.element.equals(elem))
return true;
You should note that we can easily use one of the two methods to define the other instead of writing both!
We can rewrite contains using indexOf or vice-versa quite trivally.
/**
* Returns true if the element exists in the list.
*
* @param head the reference to the head node of the list
* @param size the number of elements in the list
* @param elem the element to find the index of
* @return true if the element is found, or false otherwise
*/
public static boolean contains(Node head, int size, Object elem) {
int index = indexOf(head, size, elem);
if (index >= 0)
return true;
else
file:///Users/mumit/Sites/Classes/Summer-11/CSE-220/lectures/linked-list-notes.html Page 6 of 12
Linked lists in Java 8/11/11 1:16 PM
return false;
/**
* Inserts the new element at the given index into the list.
*
* @param head the reference to the head node of the list
* @param size the number of elements in the list
* @param index the index of the newly inserted element
* @param elem the new element to insert at the given index
* @return the reference to the head node, which will change when
* inserting in the beginning.
* @exception IndexOutOfBoundsException if the index is out-of-bounds.
*/
public static Node insert(Node head, int size, Object elem, int index) {
// Check for invalid index first. Should be between 0 and size (note:
// note size-1, since we can insert it *after* the tail node (tail
// node has an index of size-1).
if (index < 0 || index > size)
throw new IndexOutOfBoundsExceptionException();
If there was also a tail reference, in addition to head, appending to the list is very fast — it avoids having
file:///Users/mumit/Sites/Classes/Summer-11/CSE-220/lectures/linked-list-notes.html Page 7 of 12
Linked lists in Java 8/11/11 1:16 PM
If there was also a tail reference, in addition to head, appending to the list is very fast — it avoids having
to scan the entire list until the tail node (the predecessor in this case) is found.
/**
* Removes the element at the given index in the list.
*
* @param head the reference to the head node of the list
* @param size the number of elements in the list
* @param index the index of the element to remove
* @return the reference to the head node, which will change when
* removing the element at the beginning.
* @exception IndexOutOfBoundsException if the index is out-of-bounds.
*/
public static Node remove(Node head, int size, int index) {
// Check for invalid index first. Should be between 0 and size-1.
if (index < 0 || index >= size)
throw new IndexOutOfBoundsException();
// Remember the special case -- from the beginning of the list, when
// the head reference changes.
if (index == 0) {
removedNode = head;
head = head.next;
} else {
// get the predecessor node first
Node pred = nodeAt(index - 1);
removedNode = pred.next;
pred.next = removedNode.next;
}
// Help the GC
removedNode.element = null;
removedNode.next = null;
return head;
}
Copying a list
file:///Users/mumit/Sites/Classes/Summer-11/CSE-220/lectures/linked-list-notes.html Page 8 of 12
Linked lists in Java 8/11/11 1:16 PM
Copying a list
Copying the elements of a source list to destination list is simply a matter of iterating over the elements of
the source list, and inserting these elements at the end of the destination list.
/**
* Copy the source list and return the reference to the copy.
*
* @param source the head reference of the source list
* @return reference to the head of the copy
*/
public static Node copyList(Node source) {
Node copyHead = null;
Node copyTail = null;
for (Node n = source; n != null; n = n.next) {
Node newNode = new Node(n.element, null);
if (copyHead == null) {
// First node is special case - head and tail are the same
copyHead = newNode;
copyTail = copyHead;
} else {
copyTail.next = newNode;
copyTail = copyTail.next; // same as: copyTail = newNode;
}
}
return copyHead;
}
Reversing a list
Since a linked list does not support random access, it is difficult to reverse a list in-place without
changing the head reference. Instead we'll create a new list with its own head reference, and copy the
elements in the reverse order. This method does not modify the original list, so we can call it an out-of-
place method.
/**
* Reverses the list and returns the reference to the head node.
*
* @param list the list to reverse
* @param size the number of elements in the list
* @return reference to the head of the reversed list
*/
public static Node reverse(Node head, int size) {
Node newHead = null;
// We iterate over the nodes in the original list, and add a copy
// of each node to the *beginning* of the new list, which reverses
// the order.
for (Node n = head; n != null; n = n.next) {
Node newNode = new Node(n.next, null);
file:///Users/mumit/Sites/Classes/Summer-11/CSE-220/lectures/linked-list-notes.html Page 9 of 12
Linked lists in Java 8/11/11 1:16 PM
return newHead;
}
The problem with this approach is that it creates a copy of the whole list, just in the reverse order. We
would like an in-place approach instead — re-order the links instead of copying the nodes! That would of
course change the original list, and would have a new head reference (which is the reference to the tail
node in the original list).
/**
* Reverses the list and returns the reference to the head node.
*
* @param list the list to reverse
* @param size the number of elements in the list
* @return reference to the head of the reversed list
*/
public static Node reverse(Node head, int size) {
Node newHead = null;
// We iterate over the nodes in the original list, and add each
// node to the *beginning* of the new list, which reverses the
// order. Have to be careful when we iterate -- have to save the
// reference to the *next* node before changing the next link.
Node n = head;
while (n != null) {
Node nextNode = n.next; // need to do this!
return newHead;
}
/**
* Rotates the given list left by one element
*
* @param head the list to rotate left
* @param size the number of elements in the list
file:///Users/mumit/Sites/Classes/Summer-11/CSE-220/lectures/linked-list-notes.html Page 10 of 12
Linked lists in Java 8/11/11 1:16 PM
// Now append the old head to the end of this list. Find the tail,
// and add the old head after the tail.
Node tail = head;
while (tail.next != null)
tail = tail.next;
return head;
}
/**
* Rotates the given list right by one element
*
* @param head the list to rotate right
* @param size the number of elements in the list
* @return reference to the head of the rotated list
*/
public static Node rotateRight(Node head, int size) {
// Bug alert - does it work for empty lists?
// Need to find tail and it's predecessor (do you see why?)
Node p = null;
Node q = head;
while (q.next != null) {
p = tail;
q = q.next;
}
return head;
}
file:///Users/mumit/Sites/Classes/Summer-11/CSE-220/lectures/linked-list-notes.html Page 11 of 12
Linked lists in Java 8/11/11 1:16 PM
You can look at the example LinkedList.java class to see how these can be implemented. Run the
LinkedList.main() to see the output.
file:///Users/mumit/Sites/Classes/Summer-11/CSE-220/lectures/linked-list-notes.html Page 12 of 12