Upload
jack-gray
View
235
Download
5
Tags:
Embed Size (px)
Citation preview
Chapter 22Implementing lists: linked implementations
This chapter discusses
Linked lists lists built through object
references. dynamic lists. linked implementations of
structures more complex than a simple sequence.
A linked list implementation If the list is not empty
there is a first element there is a last element every element (except the last)
has a successor every element (except the first)
has a predecessor
A linked list implementation (cont.) An array structure is built by creating a
reference to the collection of list elements which are stored sequentially in memory.
A linked list is built by creating a reference to a node of the list that, in turn contains a reference to another node of the list, etc.
LinkedList class
public abstract class LinkedList implements Cloneable {
…
private class Node { /**
* Create a Node containing specified element.
* /
public Node (Object element) {
this.element = element;
this.next = null;
}
Object element;
Node next;
}
}
LinkedList class (cont.)
public abstract class LinkedList implements Cloneable {/**
* Create a Node containing specified element.
* /
protected LinkedList () {
size = 0;
first = null;
}…
private int size;
private Node first;
}
}
LinkedList class (cont.)
LinkedList class (cont.)
The component next of the last Node always has a value of null.
For the empty list, the LinkedList component first is null.
LinkedList methods:getpublic Object get (int i) {
Node p = first;
int pos = 0;
while (pos < i) {
p = p.next;
pos = pos + 1;
}
return p.element;
}
LinkedList methods:get (cont.) The variable p is initialized with a
reference to the 0-th Node of the list.
LinkedList methods:get (cont.) Each iteration of the loop assigns p
a reference to the next element of the list and increments pos.
LinkedList methods:get (Cont). Loop invariant: p references the
Node containing the element with index pos.
At loop termination pos == i p references the Node we are
looking for.
LinkedList methods:append First, find the last element of list
LinkedList methods:append (cont.) Create a new Node containing the element to
be appended Set the old last Node’s next component to
reference the new Node.
public void append (Object obj) {if (this.isEmpty()) first = new Node(obj);else { Node p = first; while (p.next != null);
p = p.next; p.next = new Node(obj);}size = size + 1;
}
LinkedList methods: remove First, find the Node in front of the
one we want to delete.
LinkedList methods: remove (cont.) Take next of the Node before the one to
be deleted and reference it the same as the next field of the Node to be deleted.
LinkedList methods: remove (cont.) Since p.next is the Node to be
deleted, p.next cannot be null. Removing the first element of the
list is a special case.
LinkedList methods: remove (cont.)
public void remove (int i) {if (i == 0) { first = first.next;else { Node p = first; int pos = 0; while (pos < i-1) {
p = p.next;pos = pos + 1;
} p.next = p.next.next;}size = size - 1;
}
LinkedList methods: add
LinkedList methods: add (cont.)public add (int i, Object obj) {
Node newElement = new Node(obj);if (i == 0) { newElement.next = first; first = newElement;} else { Node p = first; int pos = 0; while (pos < i-1) {
p = p.next;pos = pos + 1;
} newElement.next = p.next; p.next = newElement;}size = size + 1;
}
LinkedList methods:
get, append, remove, and add all require linear time on average.
Deleting the first element and inserting a new first element, are constant time operations.
We must be particularly careful of “boundary” cases: cases involving the empty list a list with one element the first or last element of a list
These may well require explicit handling.
LinkedList variations A simple change will make append a constant time operation.
We keep a reference to both the first and last elements in the list.
LinkedList variations (cont.)public append (Object obj) {
Node newElement = new Node(obj);
if (this.isEmpty())
first = newElement;
else
last.next = newElement;
last = newElement;
size = size + 1;
}
LinkedList variations (cont.) remove must now check explicitly
for the case in which the last element is deleted.
public void remove (int i) {if (size == 1) { // remove the only element first = null; last = null;} else if (i == 0) { // remove the first element first = first.next;}
LinkedList variations (cont.)
else { Node p = first; int pos = 0; while (pos < i-1) {
p = p.nextpos = pos + 1;
} p.next = p.next.next; if (i == size-1) //last element removed
last = p;}size = size - 1;
}
Header nodes
One way to eliminate special cases is to employ a header node.
It contains no element, but is always present at the front of the list. i.e. it is referenced by first.
Header nodes (cont.)
private class Header extends Node {
public Header () {
this.element = null;
this.next = null;
}
}
The LinkedList constructor creates the header.
protected LinkedList () {
size = 0;
first = new Header();
last = first;
}
Header nodes (cont.)
The method append, need not check explicitly for the empty list.
public append (Object obj) {
Node newElement = new Node(obj);
last.next = newElement;
last = newElement;
size = size + 1;
}
Circular lists In a circular list, the last node references
the first. A circular list may or may not have a
header. We can traverse the entire list starting
from any node. Care must be taken to avoid infinite
iterations or recursions.
Doubly-linked lists
Each node contains references to the preceding as well as to the following node.
Doubly-linked lists (cont.) Three components:
the list elements references to its two neighboring nodes.
public abstract class DoublyLinkedList implements Cloneable {…private int size;private Node header;private class Node { public Node (Object element) {
this.element = element;this.next = null;this.previous = null;
} Object element; Node next; Node previous;}
}
Doubly-linked lists (cont.)protected DoublyLinkedList () {
size = 0;
header = new Header();
header.next = header;
header.previous = header;
}
Doubly-linked lists (cont.) Operations are a bit more complicated
since we have two references in each node, but the combination of a circular structure and a header eliminates the need for handling most boundary cases explicitly.
DoublyLinkedList: append Set previous of the new node to
reference the old last node. Set next of the new node to reference
the header. Set previous of the header to reference
the new node. Set next component of the old last node
to reference the new node.
DoublyLinkedList: append (cont.)public void append (Object obj) {
Node newElement = new Node(obj);
Node last = header.previous;
newElement.next = header;
newElement.previous = last;
last.next = newElement;
header.previous = newElement;
size = size + 1;
}
Linked list limitations
Accessing elements by index is a linear time operation.
The get operation is linear. Therefore, any operation using the get method will be slower than in an array-based implementation.
public boolean contains (List list, Object obj) {int n = list.size();int i = 0;while (i < n && !obj.equals(list.get(i)) i = i + 1;}return i < n;
}
Linked list limitations (cont.) This method can be implemented
without get.
public boolean contains (LinkedList list, Object obj) {Node p = list.first;while (p != null && !obj.equals(p.element)) p = p.next;}return p != null;
}
Dynamic storage allocation automatic allocation: memory space for
automatic variables is allocated when the method is invoked, and reclaimed (“deallocated”) when the method completes.
Memory space for array elements is allocated when the array is created.
For a linked implementation, space required for a node is allocated when the node is created.
Garbage collection
Dynamically allocated space that can no longer be accessed is termed garbage.
If we create an object and then loose all references to the object, the memory space occupied by the object becomes garbage.
Garbage collection (cont.) The Java run-time system or interpreter
continuously looks for garbage and reclaims the space (garbage collection).
Many programming languages do not include garbage collection as part of their run-time systems.
They require programs to deallocate explicitly dynamically allocated space.
In an object-oriented environment, it is often difficult to know when an object no longer is accessible.
Garbage collection (cont.) Dangling references are references to
space that has been reclaimed mistakenly.
Such references result in errors that often are extremely difficult to track down.
We’ve covered
Linked structures nodes headers
Doubly-linked lists Circular lists Time complexities Dynamic storage allocation
garbage collection
Glossary
Glossary (cont.)