Lists and Sequences
Singly Linked List
- A singly linked list is a concrete data structure consisting of a sequence of nodes
- Each node stores
- element
- link to the next node
Stack with a Singly Linked List
- We can implement a stack with a singly linked list
- The top element is stored at the first node of the list
- The space used is O(n) and each operation of the Stack ADT takes O(1) time
Position ADT
- The Position ADT models the notion of place within a data structure where a single object is stored
- It gives a unified view of diverse ways of storing data, such as
- a cell of an array
- a node of a linked list
- Just one method:
- object element(): returns the element stored at the position
List ADT
- The List ADT models a sequence of positions storing arbitrary objects
- It establishes a before/after relation between positions
- Generic methods:
- Query methods
- Accessor methods:
- first(), last()
- before(p), after(p)
- Update methods:
- replaceElement(p, o), swapElements(p, q)
- insertBefore(p, o), insertAfter(p, o),
- insertFirst(o), insertLast(o)
- remove(p)
Doubly Linked List
- A doubly linked list provides a natural implementation of the List ADT
- Nodes implement Position and store:
- element
- link to the previous node
- link to the next node
- Special trailer and header nodes
- Performance
- The space used by a list with n elements is O(n)
- The space used by each position of the list is O(1)
- All the operations of the List ADT run in O(1) time
- Operation element() of the Position ADT runs in O(1) time
Code
package examples;
import java.util.Iterator;
import java.util.NoSuchElementException;
public class MyLinkedList<E> implements List<E> {
class LNode implements Position<E>{
E elem;
LNode prev,next;
Object creator = MyLinkedList.this;
@Override
public E element() {
return elem;
}
}
private LNode first,last;
private int size;
private LNode castToLNode(Position p){
LNode n;
try {
n = (LNode) p;
} catch (ClassCastException e) {
throw new RuntimeException("This is not a Position belonging to MyLinkedList");
}
if (n.creator == null) throw new RuntimeException("position was allready deleted!");
if (n.creator != this) throw new RuntimeException("position belongs to another MyLinkedList instance!");
return n;
}
@Override
public Position<E> first() {
return first;
}
@Override
public Position<E> last() {
return last;
}
@Override
public boolean isFirst(Position<E> p) {
return castToLNode(p)==first;
}
@Override
public boolean isLast(Position<E> p) {
return castToLNode(p)==last;
}
@Override
public Position<E> next(Position<E> p) {
LNode n = castToLNode(p);
if (n==last) throw new RuntimeException("There is no next position!");
return n.next;
}
@Override
public Position<E> previous(Position<E> p) {
LNode n = castToLNode(p).prev;
if (n==first) throw new RuntimeException("There is no previous position!");
return n.prev;
}
@Override
public E replaceElement(Position<E> p, E o) {
LNode n = castToLNode(p);
E old = n.elem;
n.elem = o;
return old;
}
@Override
public Position<E> insertFirst(E o) {
LNode n = new LNode();
n.elem = o;
n.next = first;
if (first == null) last=n;
else first.prev = n;
first = n;
size++;
return n;
}
@Override
public Position<E> insertLast(E o) {
LNode n = new LNode();
n.elem = o;
n.prev = last;
if (last == null) first=n;
else last.next = n;
last = n;
size++;
return n;
}
@Override
public Position<E> insertBefore(Position<E> p, E o) {
LNode n = castToLNode(p);
LNode newN = new LNode();
newN.elem = o;
newN.next = n;
if (n.prev==null) first = newN ;
else {
newN.prev = n.prev;
newN.prev.next = newN;
}
n.prev = newN;
size++;
return newN;
}
@Override
public Position<E> insertAfter(Position<E> p, E o) {
LNode n = castToLNode(p);
LNode newN = new LNode();
newN.elem = o;
newN.prev = n;
if (n.next==null) last = newN ;
else {
newN.next = n.next;
newN.next.prev = newN;
}
n.next = newN;
size++;
return newN;
}
@Override
public void remove(Position<E> p) {
LNode n = castToLNode(p);
size--;
n.creator = null;
if (n==first) {
first=n.next;
if (first != null) first.prev = null;
}
else n.prev.next = n.next;
if (n==last) {
last =n.prev;
if (last!=null) last.next = null;
}
else n.next.prev = n.prev;
}
@Override
public Iterator<Position<E>> positions() {
return new Iterator<Position<E>>(){
LNode currentNode = first;
@Override
public boolean hasNext() {
return currentNode != null;
}
@Override
public Position<E> next() {
if (currentNode == null) throw new NoSuchElementException("there are no more elments in this LinkedList");
LNode ret = currentNode;
currentNode = currentNode.next;
return ret;
}
};
}
@Override
public Iterator<E> elements() {
return new Iterator<E>(){
LNode currentNode = first;
@Override
public boolean hasNext() {
return currentNode != null;
}
@Override
public E next() {
if (currentNode == null) throw new NoSuchElementException("there are no more elments in this LinkedList");
LNode ret = currentNode;
currentNode = currentNode.next;
return ret.elem;
}
};
}
@Override
public int size() {
return size;
}
@Override
public boolean isEmpty() {
return size==0;
}
public static void main(String[] args) {
List<String> li = new MyLinkedList<>();
Position<String> p =li.insertFirst("hans");
li.insertFirst("beat");
p = li.insertFirst("ida");
System.out.println(li.next(li.next(p)).element());
li.insertBefore(p,"hans 1");
li.insertAfter(p, "bla");
li.remove(p);
System.out.println("--");
Iterator<Position<String>> it = li.positions();
while (it.hasNext()){
Position<String> pi = it.next();
if (pi.element().equals("bla")) li.remove(pi);
System.out.println(pi.element());
}
System.out.println("--");
Iterator<String> it2 = li.elements();
while (it2.hasNext()){
System.out.println(it2.next());
}
}
}
Sequence ADT
- The Sequence ADT is the union of the Vector and List ADTs
- Elements accessed by
- Generic methods:
- Vector-based methods:
- elemAtRank(r),
- replaceAtRank(r, o),
- insertAtRank(r, o),
- removeAtRank(r)
- List-based methods:
* first(), last(), before(p), after(p), replaceElement(p, o), swapElements(p, q), insertBefore(p, o), insertAfter(p, o), insertFirst(o), insertLast(o), remove(p)
- Bridge methods:
- Applications: generic replacement for stack, queue, vector or list, small database
Array-based Implementation
- We use a circular array storing positions
- A position object stores: Element & Rank
- Indices f and l keep track of first and last positions
Iterators
- An iterator abstracts the process of scanning through a collection of elements
- Methods of the ObjectIterator ADT:
- object object()
- boolean hasNext()
- object nextObject()
- reset()
- Extends the concept of Position by adding a traversal capability
- Implementation with an array or singly linked list
- An iterator is typically associated with an another data structure
- We can augment the Stack, Queue, Vector, List and Sequence ADTs with method:
- ObjectIterator elements()
- Two notions of iterator:
- snapshot: freezes the contents of the data structure at a given time
- dynamic: follows changes to the data structure