Linked Lists Linear collections. List Abstract Data Type List interface built-in to the collections framework The built-in List interface is a dynamic.
Post on 30-Mar-2015
226 Views
Preview:
Transcript
Linked Lists
Linear collections
List Abstract Data Type List interface built-in to the
collections framework The built-in List interface is a
dynamic linear collection that DOES support random access
This presentation uses a “simplified” List interface Our list is NOT the built-in
list! Our list does NOT support
random access Our list is dynamic
(grows/shrinks as needed)
Java List Interface (Partial listing)
void add(int index, Object element) - Inserts the specified element at the specified position in this list
boolean add(Object element) - Appends the specified element to the end of this List.
boolean addAll(int index, Collection c) - Inserts all of the elements in the specified collection into this list at the specified position.
void clear() – Removes all elements from the list
boolean equals(Object element) – Compares the specified object with this list for equality
Object get(int index) – Returns the element at the specified index
int indexOf(Object element) – Returns the index of the first occurrence of this object in the list
boolean isEmpty() – Returns true if this list contains no elements and false otherwise
Iterator iterator() – Returns an iterator over the elements of the list
int lastIndexOf(Object element) – Returns the index of the last occurrence of element in the list
Object remove(int index) – Removes and returns the element at the specified index
Object set(int index, Object element) – Replaces the item at the specified index with the specified object
int size() – Returns the number of elements in the list
Our List Interface Fundamental Methods
void addToFront(Object d) Adds object d to the front of the list
void addToTail(Object d) Adds object d to the end of the list
Object removeFirst() Removes and returns the first element of
the list An error occurs if the list is empty
Object removeLast() Removes and returns the last element of
the list An error occurs if the list is empty
Object first() Returns the first element of the list An error occurs if the list is empty
Object last() Returns the last element of the list An error occurs if the list is empty
interface List{ public void addToFront(Object o); public void addToTail(Object o); public Object removeFirst(); public Object removeLast(); public Object first(); public Object last(); public int size(); public boolean isEmpty();}
List ADT
()errortheList.removeLast()
()“B”theList.removeLast()
( “B”)“A ”theList.removeLast()
( “B”,”A ”)“Y ”theList.removeLast()
( “B”,”A ”,”Y ”)“X ”theList.removeFirst()
( “X ”,”B”,”A ”,”Y ”)“Y ”theList.last()
( “X ”,”B”,”A ”,”Y ”)“X ”theList.first()
( “X ”,”B”,”A ”,”Y ”)“X ”theList.first()
( “X ”,”B”,”A ”,”Y ”)nonetheList.addToTail(“Y ”)
( “X ”,”B”,”A ”)nonetheList.addToFront(“X ”)
( “B”, “A ”)nonetheList.addToFront(“B”)
( “A ”)nonetheList.addToFront(“A ”)
ListOutputOperation
List Implementation Sequential Implementation (using arrays)
Use an array to hold data elements Has a “fixed” capacity with (probably many) wasted slots. Insertion at
the “beginning” of the list is slow (linear or O(n) performance). Keep track of the size of the list explicitly. Identical to a Vector
data:size: 5
List Object
index 0 index 11
Data Objects
Java Vector Class Hierarchy
Vector
AbstractList
AbstractCollection
Object
List InterfaceList Interface
Collection InterfaceCollection Interface
Array List Performance
Method Run-time performance
void add(int index, Object element) O(n)boolean add(Object element) O(1)void clear() O(1) or O(n)Object get(int index) O(1)int indexOf(Object element) O(n)Object remove(int index) O(n)Object set(int index, Object element) O(1)
List Implementation Singly-Linked Implementation:
Uses a recursively-defined “list node” structure to hold data elements Keep track of only the “head” node and can access all other nodes by
following a “link” to the “next” node Insertions and removals are done by changing the links. No shifting of
data is ever required
Next
Data
Node Object Data Object
headhead
List Object
Next
Data
Next
Data
Singly Linked Listclass SListNode {
private SListNode next;
private Object data;
SListNode(Object d) {
data = d;
next = null;
}
SListNode(Object d, SListNode n) {
data = d;
next = n;
}
public SListNode getNext() {
return next;
}
public Object getData() {
return data;
}
public void setNext(SListNode n) {
next = n;
}
public void setData(Object d) {
data = d;
}
}
Each node is a “link” in a chain of “nodes”.
Each node can only see “forward”.
Each node contains a data element.
Singly Linked Listclass SinglyLinkedList implements List{ private SListNode head; private int size;
SinglyLinkedList() { head = null; size = 0; }
public void addToFront(Object o) { … } public void addToTail(Object o) { … } public Object removeFirst() { … } public Object removeLast() { … }} The list keeps track of the first link (called
the “head”) from which all other links can be accessed.
interface List{ public void addToFront(Object o); public void addToTail(Object o); public Object removeFirst(); public Object removeLast(); public Object first(); public Object last(); public int size(); public boolean isEmpty();}
Singly Linked List Insertion
headhead
List Object
Next
Data
Next
Data
Next
Data
Node Object Data Object
Inserting an item means changing links (not shifting data)
How do we insert an item at the front of this list?
Singly Linked List Insertion
headhead
List Object
Next
Data
Next
Data
Next
Data
Node Object
Data Object
public void addToFront(Object o) {
head = new SListNode(o, head);
size++;
}
public void addToFront(Object o) {
head = new SListNode(o, head);
size++;
}
Show me the code!
Singly Linked List Removal
headhead
List Object
Next
Data
Next
Data
Next
Data
Node Object
Data Object
Removing an item means changing links (not shifting data)
How do we remove an item from the front of this list?
Singly Linked List Removal
headhead
List Object
Next
Data
Next
Data
Next
Data
Node Object
Data Object
public Object removeFirst() {
if(isEmpty()) {
throw new NoSuchElementException();
}
SListNode node = head;
head = head.getNext();
size--;
return node.getData();
}
public Object removeFirst() {
if(isEmpty()) {
throw new NoSuchElementException();
}
SListNode node = head;
head = head.getNext();
size--;
return node.getData();
}nullPointerException!
Aaargh!!!
Singly Linked List Example(lists are entertaining and fun!)
public Object removeFirst() {
if(isEmpty()) {
throw new NoSuchElementException();
}
SListNode node = head;
head = head.getNext();
size--;
return node.getData();
}
public Object removeFirst() {
if(isEmpty()) {
throw new NoSuchElementException();
}
SListNode node = head;
head = head.getNext();
size--;
return node.getData();
}
public void addToFront(Object o) {
head = new SListNode(o, head);
size++;
}
public void addToFront(Object o) {
head = new SListNode(o, head);
size++;
}
List list = new SinglyLinkedList();
for(int i=0; i<3; i++) {
list.addToFront(new Integer(i));
}
while(!list.isEmpty()) {
System.out.println(list.removeFirst());
}
List list = new SinglyLinkedList();
for(int i=0; i<3; i++) {
list.addToFront(new Integer(i));
}
while(!list.isEmpty()) {
System.out.println(list.removeFirst());
}
Linked List Insertion
Next
Data
Node Object Data Object
headhead
List Object
Next
Data
Next
Data
What needs to happen to insert at the end of this list?
Inserting at the end means changing the link of the last node!
Since we only “know” about the first node we have to navigate through the list until the last node is found. We then change its link.
Singly Linked List Insertion
public void addToTail(Object value) {
SListNode newNode = new SListNode(value, null);
if(isEmpty()) {
head = newNode;
} else {
SListNode n = head;
while(n.getNext() != null) {
n = n.getNext();
}
n.setNext(newNode);
}
size++;
}
public void addToTail(Object value) {
SListNode newNode = new SListNode(value, null);
if(isEmpty()) {
head = newNode;
} else {
SListNode n = head;
while(n.getNext() != null) {
n = n.getNext();
}
n.setNext(newNode);
}
size++;
}
Um, Ah, ?, …, getNext!
Inserting at the end means changing the link of the last node!
Since we only “know” about the first node we have to navigate through the list until the last node is found. We then change its link.
Singly Linked List Removal
public Object removeLast() {
if(isEmpty()) {
throw new NoSuchElementException();
}
SListNode tmp = head, prev = null;
while(tmp.getNext() != null) {
prev = tmp;
tmp = tmp.getNext();
}
if(prev == null) {
head = null;
} else {
prev.setNext(null);
}
size--;
return tmp.getData();
}
public Object removeLast() {
if(isEmpty()) {
throw new NoSuchElementException();
}
SListNode tmp = head, prev = null;
while(tmp.getNext() != null) {
prev = tmp;
tmp = tmp.getNext();
}
if(prev == null) {
head = null;
} else {
prev.setNext(null);
}
size--;
return tmp.getData();
}
Removing at the end means changing the link of the second-to-last node!
Since we only “know” about the first node we have to navigate through the list until the second-to-last node is found. We then change its link.
Singly Linked List Example(lists are entertaining and fun!)
List list = new SinglyLinkedList();
for(int i=0; i<3; i++) {
list.addToTail(new Integer(i));
}
while(!list.isEmpty()) {
System.out.println(list.removeLast());
}
List list = new SinglyLinkedList();
for(int i=0; i<3; i++) {
list.addToTail(new Integer(i));
}
while(!list.isEmpty()) {
System.out.println(list.removeLast());
}
public void addToTail(Object value) {
SListNode newNode = new SListNode(value, null);
if(isEmpty()) {
head = newNode;
} else {
SListNode n = head;
while(n.getNext() != null) {
n = n.getNext();
}
n.setNext(newNode);
}
size++;
}
public void addToTail(Object value) {
SListNode newNode = new SListNode(value, null);
if(isEmpty()) {
head = newNode;
} else {
SListNode n = head;
while(n.getNext() != null) {
n = n.getNext();
}
n.setNext(newNode);
}
size++;
}
Singly Linked List Example(lists are entertaining and fun!)
List list = new SinglyLinkedList();
for(int i=0; i<3; i++) {
list.addToTail(new Integer(i));
}
while(!list.isEmpty()) {
System.out.println(list.removeLast());
}
List list = new SinglyLinkedList();
for(int i=0; i<3; i++) {
list.addToTail(new Integer(i));
}
while(!list.isEmpty()) {
System.out.println(list.removeLast());
}
public Object removeLast() {
if(isEmpty()) {
throw new NoSuchElementException();
}
SListNode tmp = head, prev = null;
while(tmp.getNext() != null) {
prev = tmp;
tmp = tmp.getNext();
}
if(prev == null) {
head = null;
} else {
prev.setNext(null);
}
size--;
return tmp.getData();
}
public Object removeLast() {
if(isEmpty()) {
throw new NoSuchElementException();
}
SListNode tmp = head, prev = null;
while(tmp.getNext() != null) {
prev = tmp;
tmp = tmp.getNext();
}
if(prev == null) {
head = null;
} else {
prev.setNext(null);
}
size--;
return tmp.getData();
}
Singly Linked List Variations There are variations on implementation that make coding somewhat
simpler use a sentinel node as the head to simplify the code (doesn’t do much for singly-
linked lists) sentinel node is always present (even in an empty list) sentinel node stores no data sentinel node serves only as a “bookend”
public SinglyLinkedList implements List {
private SListNode head;
private int size;
SinglyLinkedList() {
head = new SListNode(null, null);
size = 0; }
…
}
public SinglyLinkedList implements List {
private SListNode head;
private int size;
SinglyLinkedList() {
head = new SListNode(null, null);
size = 0; }
…
}
If in an empty list head is not null then the list uses sentinel nodes.
Sentinal Node Examplepublic void addToFront(Object o) {
head.setNext(new SListNode(o, head.getNext());
size++;
}
public void addToFront(Object o) {
head.setNext(new SListNode(o, head.getNext());
size++;
}
public Object removeFirst() {
if(isEmpty()) {
throw new NoSuchElementException();
}
SListNode node = head.getNext();
head.setNext(head.getNext().getNext());
size--;
return node.getData();
}
public Object removeFirst() {
if(isEmpty()) {
throw new NoSuchElementException();
}
SListNode node = head.getNext();
head.setNext(head.getNext().getNext());
size--;
return node.getData();
}
public void addToFront(Object o) {
head = new SListNode(o, head);
size++;
}
public void addToFront(Object o) {
head = new SListNode(o, head);
size++;
}
public Object removeFirst() {
if(isEmpty()) {
throw new NoSuchElementException();
}
SListNode node = head;
head = head.getNext();
size--;
return node.getData();
}
public Object removeFirst() {
if(isEmpty()) {
throw new NoSuchElementException();
}
SListNode node = head;
head = head.getNext();
size--;
return node.getData();
}
Sentinel Nodes No Sentinel Nodes
Singly-Linked List Performance
Method Run-time performance
void add(int index, Object element) O(n)
boolean add(Object element) O(1)void clear() O(1)
Object get(int index) O(n)int indexOf(Object element) O(n)
Object remove(int index) O(n)Object set(int index, Object
element) O(n)
List Implementation Doubly-Linked Implementation:
Uses a recursively-defined “list node” structure to hold data elements Has dynamic capacity with little “wasted” memory Keep track of the “head” and “tail” nodes and can access all other nodes “Natural” operations are from the head and tail since they are “fast”
Next
Prev
Head Node
Data
Next
Prev
Data
Next
Prev
Data
Tail Node
Doubly Linked Listclass DListNode { private DListNode previous, next; private Object data;
DListNode(Object d) { data = d; next = null; previous = null; }
DListNode(Object d, DListNode p, DListNode n) { data = d; previous = p; next = n; }
// accessors and mutators}
Each node is a “link” in a chain of “nodes”.
Each node can see “forward” AND “backward”
Each node contains a data element.
Doubly Linked Listclass DoublyLinkedList implements List{ private DListNode head, tail; private int size;
DoublyLinkedList() { head = null; tail = null; size = 0; }
public void addToFront(Object o) { … } public void addToTail(Object o) { … } public Object removeFirst() { … } public Object removeLast() { … } // other methods}
The list keeps track of the first link (called the “head”) from which all other links can be accessed.
interface List{ public void addToFront(Object o); public void addToTail(Object o); public Object removeFirst(); public Object removeLast(); public Object first(); public Object last(); public int size(); public boolean isEmpty();}
Add To Front Operation
Next
Prev
Head Node
Data
Next
Prev
Data
Next
Prev
Data
Tail Node
tail
head
size: 3
public void addToFront(Object o) {
head = new DListNode(o, null, head);
if(head.getNext() != null) {
head.getNext().setPrevious(head);
}
if(tail == null) tail = head;
size++;
}
public void addToFront(Object o) {
head = new DListNode(o, null, head);
if(head.getNext() != null) {
head.getNext().setPrevious(head);
}
if(tail == null) tail = head;
size++;
}
What needs to be done to insert an item at the head of this list?
Remove First Operation
Next
Prev
Data
Next
Prev
Data
Next
Prev
Data
tail
head
size: 3
public Object removeFirst() {
if(isEmpty()) {
throw new NoSuchElementException();
}
Object result = head.getData();
head = head.getNext();
if(head != null) {
head.setPrevious(null);
} else {
tail = null;
}
size--;
return result;
}
public Object removeFirst() {
if(isEmpty()) {
throw new NoSuchElementException();
}
Object result = head.getData();
head = head.getNext();
if(head != null) {
head.setPrevious(null);
} else {
tail = null;
}
size--;
return result;
}
What needs to be done to remove an item at the head of this list?
Front Operations Examplepublic void addToFront(Object o) {
head = new DListNode(o, null, head);
if(head.getNext() != null) {
head.getNext().setPrevious(head);
}
if(tail == null) tail = head;
size++;
}
public void addToFront(Object o) {
head = new DListNode(o, null, head);
if(head.getNext() != null) {
head.getNext().setPrevious(head);
}
if(tail == null) tail = head;
size++;
}
public Object removeFirst() {
if(isEmpty()) {
throw new NoSuchElementException();
}
Object result = head.getData();
head = head.getNext();
if(head != null) {
head.setPrevious(null);
} else {
tail = null;
}
size--;
return result;
}
public Object removeFirst() {
if(isEmpty()) {
throw new NoSuchElementException();
}
Object result = head.getData();
head = head.getNext();
if(head != null) {
head.setPrevious(null);
} else {
tail = null;
}
size--;
return result;
}List list = new DoublyLinkedList();
for(int i=0; i<3; i++) {
list.addToFront(new Integer(i));
}
while(!list.isEmpty()) {
System.out.println(list.removeFirst());
}
List list = new DoublyLinkedList();
for(int i=0; i<3; i++) {
list.addToFront(new Integer(i));
}
while(!list.isEmpty()) {
System.out.println(list.removeFirst());
}
Add To Tail Operation
public void addToTail(Object o) {
tail = new DListNode(o, tail, null);
if(tail.getPrevious() != null) {
tail.getPrevious().setNext(tail);
}
if(head == null) head = tail;
size++;
}
public void addToTail(Object o) {
tail = new DListNode(o, tail, null);
if(tail.getPrevious() != null) {
tail.getPrevious().setNext(tail);
}
if(head == null) head = tail;
size++;
}
public void addToFront(Object o) {
head = new DListNode(o, null, head);
if(head.getNext() != null) {
head.getNext().setPrevious(head);
}
if(tail == null) tail = head;
size++;
}
public void addToFront(Object o) {
head = new DListNode(o, null, head);
if(head.getNext() != null) {
head.getNext().setPrevious(head);
}
if(tail == null) tail = head;
size++;
}
Since it is a doubly-linked list – the list “looks” the same both forward and backward.
The methods are symmetrical!
Remove From Tail Operationpublic Object removeFirst() {
if(isEmpty()) {
throw new NoSuchElementException();
}
Object result = head.getData();
head = head.getNext();
if(head != null) {
head.setPrevious(null);
} else {
tail = null;
}
size--;
return result;
}
public Object removeFirst() {
if(isEmpty()) {
throw new NoSuchElementException();
}
Object result = head.getData();
head = head.getNext();
if(head != null) {
head.setPrevious(null);
} else {
tail = null;
}
size--;
return result;
}
public Object removeLast() {
if(isEmpty()) {
throw new NoSuchElementException();
}
Object result = tail.getData();
tail = tail.getPrevious();
if(tail != null) {
tail.setNext(null);
} else {
head = null;
}
size--;
return result;
}
public Object removeLast() {
if(isEmpty()) {
throw new NoSuchElementException();
}
Object result = tail.getData();
tail = tail.getPrevious();
if(tail != null) {
tail.setNext(null);
} else {
head = null;
}
size--;
return result;
}
The remove methods are also highly symmetrical.
public Object removeLast() {
if(isEmpty()) {
throw new NoSuchElementException();
}
Object result = tail.getData();
tail = tail.getPrevious();
if(tail != null) {
tail.setNext(null);
} else {
head = null;
}
size--;
return result;
}
public Object removeLast() {
if(isEmpty()) {
throw new NoSuchElementException();
}
Object result = tail.getData();
tail = tail.getPrevious();
if(tail != null) {
tail.setNext(null);
} else {
head = null;
}
size--;
return result;
}
Tail Operations Example
List list = new DoublyLinkedList();
for(int i=0; i<3; i++) {
list.addToTail(new Integer(i));
}
while(!list.isEmpty()) {
System.out.println(list.removeLast());
}
List list = new DoublyLinkedList();
for(int i=0; i<3; i++) {
list.addToTail(new Integer(i));
}
while(!list.isEmpty()) {
System.out.println(list.removeLast());
}
public void addToTail(Object o) {
tail = new DListNode(o, tail, null);
if(tail.getPrevious() != null) {
tail.getPrevious().setNext(tail);
}
if(head == null) head = tail;
size++;
}
public void addToTail(Object o) {
tail = new DListNode(o, tail, null);
if(tail.getPrevious() != null) {
tail.getPrevious().setNext(tail);
}
if(head == null) head = tail;
size++;
}
Doubly-Linked List PerformanceMethod Run-time performance
void add(int index, Object element) O(n)
boolean add(Object element) O(1)void clear() O(1)
Object get(int index) O(n)int indexOf(Object element) O(n)
Object remove(int index) O(n)Object set(int index, Object
element) O(n)
List Implementation Sentinel nodes:
Implementation technique to simplify the code Can be used with either doubly or singly linked lists Head (and tail) nodes are “dummy” nodes that hold no data Head (and tail) nodes never change and are always present
Next
Prev
Head Node
Data
Next
Prev
Data
Next
Prev
Data
Tail Node
“Sentinel” nodes contain no data
A Simple Problem Problem: Given a List – write a method to print the
contents of the list without modifying the list!
interface List{ public void addToFront(Object o); public void addToTail(Object o); public Object removeFirst(); public Object removeLast(); public Object first(); public Object last(); public int size(); public boolean isEmpty();}
interface List{ public void addToFront(Object o); public void addToTail(Object o); public Object removeFirst(); public Object removeLast(); public Object first(); public Object last(); public int size(); public boolean isEmpty();}
public void printList(List list) { for(int i=0; i<list.size(); i++) { Object tmp = list.removeFirst(); System.out.println(tmp); list.addToTail(tmp); }}
public void printList(List list) { for(int i=0; i<list.size(); i++) { Object tmp = list.removeFirst(); System.out.println(tmp); list.addToTail(tmp); }}
Enumerations!!!
A better solution is to use an Enumeration An interface in the “java.util” package Gives sequential read-only access to the contents of a linear
collection Don’t need to know anything about the collection implementation
public void printList(List list) { Enumeration e = list.elements(); while(e.hasMoreElements()) { System.out.println(e.nextElement()); }}
public void printList(List list) { Enumeration e = list.elements(); while(e.hasMoreElements()) { System.out.println(e.nextElement()); }}
interface Enumeration { public boolean hasMoreElements(); public Object nextElement();}
interface Enumeration { public boolean hasMoreElements(); public Object nextElement();}
Enumerationclass SinglyLinkedList implements List { private SListNode head; private int size;
// other methods here
Enumeration elements() { … }}
class SinglyLinkedList implements List { private SListNode head; private int size;
// other methods here
Enumeration elements() { … }}
class SinglyLinkedListEnumeration implements Enumeration { private SListNode current;
SinglyLinkedListEnumeration(SListNode n) { current = n; }
public boolean hasMoreElements() { return current != null; }
public Object nextElement() { if(!hasMoreElements()) { throw new NoSuchElementException(); } Object result = current.getData();
current = current.getNext(); return result; }}
class SinglyLinkedListEnumeration implements Enumeration { private SListNode current;
SinglyLinkedListEnumeration(SListNode n) { current = n; }
public boolean hasMoreElements() { return current != null; }
public Object nextElement() { if(!hasMoreElements()) { throw new NoSuchElementException(); } Object result = current.getData();
current = current.getNext(); return result; }}
Java LinkedList Class Hierarchy
LinkedList
AbstractSequentialList
AbstractList
AbstractCollection
Object
top related