L14. Singly and Doubly Linked List
L14. Singly and Doubly Linked List
Linked List
Reading: Chapter 3
Data Structures and
Algorithms in JAVA
Singly Linked List
• Arrays are nice and simple for storing things in a certain order, but
they have drawbacks:
– They are not very adaptable
– For instance, we have to fix the size n of an array in advance, which makes
resizing an array difficult
– Insertions and deletions are difficult because elements need to be shifted
around
• We now explore the implementation of an alternate sequence,
known as the singly linked list
• A linked list, in its simplest form, is a collection of nodes that
together form a linear ordering
16-2
References to same type
• Objects can point or refer to different type objects as fields
• Also, objects can refer to same type objects as fields
• What would happen if we had a class that declared one of its own
type as a field?
Pseudocode
Class Node Data portion of Node
dataType data1;
dataType data2;
• ? Will be replaced with a reference to a node which means this box does not have
data or next field.
• We do not have the actual node yet. To have it, we should call new:
elem next
list = new IntNode(); list 0
• .
IntNode default construct initializes the fields as:
– Data initialized to 0
– Next initialized to null ( in these slides / indicates null)
public class ConstructList1 {
public static void main(String[] args) {
IntNode list = new IntNode();
List node
list.elem = 42; tester
list.next = new IntNode();
list.next.elem = -3; example
list.next.next = new IntNode();
list.next.next.elem = 17;
list.next.next.next = null;
System.out.print(list.elem);
System.out.print(" " + list.next.elem);
System.out.println(" " + list.next.next.elem);
}
}
42 -3 null
Output
42 -3 17
Node with constructor
public class IntNode {
int elem;
IntNode next;
public IntNode() {
elem = 0; // this(0, null)
next = null;
}
42 -3 null
Output
42 -3 17
References vs. objects
variable = value;
a variable (left side of = ) is an arrow (the base of an arrow)
a value (right side of = ) is an object (a box; what an arrow points at)
• For the list at right: dat nex
– q = a.next; p a t
means to make q point at 2 20
– a.next = p; dat nex 2 dat nex
a
means to adjust where 1 points a t1 a t
Reassigning references 10 20
q
• a.next = b.next;
– "Make the variable a.next data next data next
a
refer to the same value as
10 20
b.next."
– Or, "Make a.next point to the data next data next
same place that b.next points." b
30 40
Traversing a list?
• Suppose we have a long chain of list nodes:
InLinkedtList
IntNode IntNode IntNode
head ele nex ele nex ele nex
empty() m t m t m t
addFirst(value)
first() 42 -3 17
removeFirst()
toString()
A IntLinkedList class
public class IntLinkedList {
private IntNode head;
public IntLinkedList() {…}
public void addFirst(int value){…}// add to front of list
public boolean isEmpty(){…} // is list empty?
public int first(){…} // get front element
public void removeFirst(){…} // remove front item list
public String toString(){…}
};
Adding to an empty list or to its front
• Before adding -3, list is empty:
head ele next
m
head -3
ele next
v m
42
– To add -3, we must create a new node and attach it to the list.
head = new IntNode(-3);
– To add 42 to the front, we must create a new node and attach it
to the list.
IntNode v = new IntNode(42);
v.next = head;
head = v; public void addFirst(int value){
IntNode v = new IntNode(value);
v.next = head;
head = v;
}
Getting/removing the front node && isEmpty
• To get the front node element: return head.elem
ele next ele next ele next
m m m
head
42 -3
public int first()17{
return head.elem;
}
IntLinkedList list;
list = new IntLinkedList();
List.addFirst(5);
list
head
ele next
ele next m
empty() ele next m
addFirst(value)
first() m 43
removeFirst(i) 143
toString()
243
21
Adding to the tail of the list
• Consider following list:
data next data next
head 42 -3
data next
current v 17
– To add a node, we must modify the next reference of the node before the
place you want to add/change
IntNode v = new IntNode(17);
IntNode current = head;
while (current.next != null) {
current = current.next;
}
public void addLast(int value){
Current.next = v; IntNode v = new IntNode(value);
if (head==null)
head=v;
else{
IntNode current = head;
while (current.next!=null){
current=current.next;
O(N) }
current.next=v;
}
}
Adding to the tail of the list using a tail reference
public class IntLinkedList {
• Another less costly approach is to private IntNode head;
add a tail reference that keeps private IntNode tail;
track of the last node: …
}
v
head tail tail
v
• In order to delete a node, we need to update the next link of the
node immediately preceding the deleted node
• To add a node between two nodes, we first point to the node before
the location of the new node (Example: to add at index 2)
IntNode v = new IntNode(5);
IntNode before = head;
for (int i = 0; i < index - 1; i++)
before = before.next;
v.next = before.next;
before.next = v;
remove a node between two nodes
// Removes value at given index from list.
// Precondition: 0 <= index < size()
public void remove(int index){
if (index == 0) {
// special case: removing first element
head = head.next;
} else {
IntNode current = head;
// removing from elsewhere in the list
for (int i = 0; i < index - 1; i++) {
current = current.next;
}
current.next = current.next.next;
}
}
trailer
header
};
trailer
header
30
Doubly Linked List- DLinkedList
public class DLinkedList { // doubly linked list
private DNode header;
private DNode trailer;
public DLinkedList(){ header = null; trailer = null;} // constructor
public boolean isEmpty(){ return ((header==null) && (trailer==null));}
public String front(){ return header.elem; }
public String back(){ return trailer.elem; }
public void addFront(String e){} // add to front of list
public addBack(String e){} // add to back of list
public removeFront(){} // remove from front
public removeBack(){} // remove from back
};
trailer
header
value
next null
null prev
trailer
header
v
trailer
v
header
33
Doubly Linked List-Deletion
public void removeFront()
{
if (header.next == NULL) //if only one item in the list
trailer = NULL;
else
header.next.prev = NULL;
header = header.next;
}
trailer
header
trailer
header
trailer
header
current
36
Doubly Linked List
•Backward Traversal: start at the last node until the beginning of the
list
DNode current =trailer;
while (current != NULL)
current = current.prev;
trailer
header
current
37
Doubly Linked List
Add a node at a specific insertion point in the list
DNode v = new DNode(e);
DNode current = header;
trailer
header current
next
prev
v 38
Implementing Singly Linked List of String Nodes
• Similar to what we have done with IntNode and IntLinkedNode,
we define two classes StringNode and StringLinkedList
– member elem in StringNode stores the element stored in this node, which
in this case is a string and memember next stores a pointer to the next node
39
Inner Classes
• We make StringNode class an inner class in StringLinkedList
• inner classes are convenient for insignificant classes.
– If inner class is defined inside an enclosing class, but outside its methods, it is
available to all methods of enclosing class
– Trivial classes can be defined inside a method
– Compiler turns an inner class into a regular class file
– Methods of inner classes can access variables and fields from the surrounding
scope.
Declared inside a method
Declared inside the class class OuterClassName {
class OuterClassName { method signature {
// methods . . .
// fields class InnerClassName
accessSpecifier class InnerClassName {
// methods
{ // fields
// methods }
// fields . . .
} }
. . . . . .
} 40
}
Implementing a Singly String Linked List
public class StringLinkedList {
private static class StringNode {
private String element; // reference to the element stored at this node
private StringNode next; // reference to the subsequent node in the list
private StringNode head = null; // head node of the list (or null if empty)
private StringNode tail = null; // last node of the list (or null if empty)
private int size = 0; // number of nodes in the list
41
Inserting an Element at the Head of a Singly Linked List
Algorithm addFirst(e):
newest = Node(e) {create new node instance storing reference to element e}
newest.next = head {set new node’s next to reference the old head node}
head = newest {set variable head to reference the new node}
size = size+1 {increment the node count}
42
Inserting an Element at the Tail of a Singly Linked List
Algorithm addLast(e):
newest = Node(e) {create new node instance storing reference to element e}
newest.next = null {set new node’s next to reference the null object}
tail.next = newest {make old tail node point to new
node}
tail = newest {set variable tail to reference the new node}
size = size+1 {increment the node count}
43
Implementing a Singly String Linked List
/** Constructs an initially empty list. */
public StringLinkedList() {}
Algorithm removeFirst():
if head == null then
the list is empty.
head = head.next {make head point to next node (or
null)}
size = size−1 {decrement the node count}
• We cannot delete the last node using a tail reference.
• we must access the node before the last node in order to remove the last node.
• We cannot reach the node before the tail by following next links from the tail.
45
Implementing a Singly String Linked List
public String removeFirst() { // removes and returns the first element
if (isEmpty())
return null; // nothing to remove
String answer = head.getElement();
head = head.getNext(); // will become null if list had only one node
size--;
if (size == 0)
tail = null; // special case as list is now empty
return answer;
}
public String first() { // returns (but does not remove) the first element
if (isEmpty())
return null;
return head.getElement();
}
public String last() { // returns (but does not remove) the last element
if (isEmpty())
return null;
return tail.getElement();
}
46
public String toString() {
if (isEmpty()) { Implementing a
return "[]";
} else { Singly String
String result = "[" + first();
Node current = head.getNext(); Linked List
while (current != null) {
result += ", " + current.getElement();
current = current.getNext();
}
result += "]";
return result;
}
}
} // end of StringLinkedList class
– By putting the Type in < >, you are demanding that any client that
constructs your object must supply a type parameter.
– The rest of your class's code can refer to that type by name.
...
}
• For a generic linked list, the node class must also accept the type
parameter E
Implementing a Generic Linked List
public class LinkedList<E> {
private static class Node<E> {
private E element; // reference to the element stored at this node
private Node<E> next; // reference to the subsequent node in the list
private Node<E> head = null; // head node of the list (or null if empty)
private Node<E> tail = null; // last node of the list (or null if empty)
private int size = 0; // number of nodes in the list
53
Implementing a Generic Linked List
/** Constructs an initially empty list. */
public LinkedList() {}
public E first() { // returns (but does not remove) the first element
if (isEmpty())
return null;
return head.getElement();
}
public E last() { // returns (but does not remove) the last element
if (isEmpty())
return null;
return tail.getElement();
}
55
Implementing a Generic Linked List
public String toString() {
if (isEmpty()) {
return "[]";
} else {
String result = "[" + first();
Node<E> current = head.getNext();
while (current != null) {
result += ", " + current.getElement();
current = current.getNext();
}
result += "]";
return result;
}
}
}
56
Implementing a Singly String Linked List
public class ListClient {
57