Linked Lists - Laboratory for Interactive Learning
Download
Report
Transcript Linked Lists - Laboratory for Interactive Learning
Linked Lists
Chapter 4
Linked Structures: Motivations
• Arrays have fixed size
– Problematic for data structures of arbitrary size
• Arrays order items physically
– Must perform expensive shift when inserting or
deleting
• Linked structures (such as lists) can
– Grow dynamically
– Handle insertions and deletions without shifting
• Draw a few examples …
Faking it with “Resizable” Arrays
• Can allocate a new array and copy into it to
make the array appear to be resizable
• Good growth policy: new array is double the
previous size.
– In 311 you’ll see that the amortized cost is much
lower than fixed increment growth.
• java.util.Vector does this
• But the copying is expensive: in many
applications, linked lists grow with lower cost
Object References
• Variables that reference objects
• Diagram memory and compare:
int p = 0;
Integer p = new Integer(0); // only if you’ll use it
• Example:
Integer p; // defaults to null
Integer q;
p = new Integer(5);
p = new Integet(6);
q = p;
q = new Integer(9);
p = null;
q = p;
Garbage Collection
What happens to objects that have no
references to them?
• They are inaccessible to the program
• Java system will remove them and recycle the
memory (usually when low on memory)
• How this is done is up to the implementation
• I’ll describe two approaches:
– Reference counting (has problems with cycles)
– Mark and sweep, or tracing
• Compaction is also important
More on Object References
• Equality operators == and != compare values
of references, not the objects
MyNumber x
MyNumber y
MyNumber z
x == y; //
= new MyNumber(9);
= new MyNumber(9);
= x;
?
x == z; // ?
• Parameter passing is reference based: value
of actual reference is copied to formal
parameter
• new may throw exception if unable to
instantiate object
Reference-Based Linked Lists
Out of what do we make linked lists?
• A node consists of a data item and a
reference to a node
• As usual, declare these data fields private and
provide assessor and mutator methods
• To be generically useful, data items can be
Object references.
Your Basic Generic Node
public class Node {
private Object item;
private Node next;
public Node(Object newItem) {
item = newItem;
next = null;
} // end constructor
public Node(Object newItem,
Node nextNode) {
item = newItem;
next = nextNode;
} // end constructor
public void setItem(Object
newItem) {
item = newItem;
} // end setItem
public Object getItem() {
return item;
} // end getItem
public void setNext(Node
nextNode) {
next = nextNode;
} // end setNext
public Node getNext() {
return next;
} // end getNext
} // end class Node
Node Example
• Work this out on paper:
Node n = new Node(new Integer(1));
Node p = new Node(new Integer(3), n);
• Let’s keep going:
n.setNext(new Node(new Integer(5));
(n.getnext()).setItem(4)
(p.getnext()).setItem(new Node(new Integer(2));
• Why not? Nodes are Objects too!
Referencing and Traversing a List
• Usually we keep track of the front of the list
with a reference variable:
Node head;
// code to build the list goes here
• Suppose head references a list of objects.
We can process each item as follows:
for (Node curr = head; curr != null; curr =
curr.getNext()) {
// do something to curr.getItem(), e.g.:
System.out.println(curr.getItem());
}
Deleting from a Linked List
•
•
•
•
•
(first try)
Suppose we want to delete a node from a list
It is referenced by a variable curr
Let’s do it …
Oops! We can’t get to the node before it!
We could have stopped curr one node earlier
and done:
curr.setNext((curr.getNext).getNext);
• But we usually use two references to avoid
chains of getNext.getNext …
Deleting from a Linked List
• To delete a node from a list, we must modify
the previous node to reference the next node
• So we use
prev = the node before the one to delete
curr = the node we want to delete
prev.setNext(curr.getNext());
• What about the first node?
head = head.getNext();
• Let’s not forget garbage collection:
curr.setItem(null); // if we don’t plan to use
curr.setNext(null); // it elsewhere
curr = null;
Inserting Into a Linked List
• Suppose we want to put a node between two
nodes. We must
– modify the previous node to reference the new
node
– keep track of the next node so the new node can
point to it
prev = the node after which we want to insert
curr = the node before which we want to insert
newNode = the node we want to insert
newNode.setNext(curr);
prev.setNext(newNode);
• Anything else?
More Inserting
• What about inserting at the front?
newNode.setNext(head);
head = newNode;
• Or inserting at the end?
– The previous code actually works if curr is null,
which it will be if curr == prev.getNext();
newNode.setNext(curr);
prev.setNext(newNode);
• OK, how do we find prev and curr?
Finding your place in a list
• Gist: after checking for special case at head
of list, move prev and curr together with
prev = curr;
curr = curr.getNext();
until you get to where you want to be, or you
reach the end of the list
• “Where you want to be” is dependent on the
application
• Common example: sorted list. Here’s how …
Finding Place in Sorted List of int
• Two ways of finding newValue’s place
prev = null;
curr = head;
while ((curr != null)
&& (newValue > curr.getItem())) {
prev = curr;
curr = curr.getNext();
} // ALTERNATIVELY:
for (prev = null, curr = head;
(curr != null) && (newValue > curr.getItem());
prev = curr, curr = curr.getNext()) {}
• At this point what does prev == null imply?
curr == null?
• Why must curr != null go first?
Will > work on a list of Objects?
• Nah.
• If the class implements java.lang.Comparable:
public class MyClass implements
java.lang.Comparable {
...
public int compareTo(Object rhs) {
// returns 0 if “equal”
// returns 1 if rhs is “greater”
// returns -1 if rhs is “less”
}
}
• Then we can write:
for (prev = null, curr = head;
(curr != null) &&
(newValue.compareTo(curr.getItem()) > 0);
prev = curr, curr = curr.getNext()) {}
Reference-based Implemenation
of ADT List
public class ListReferenceBased implements ListInterface {
// reference to linked list of items
private Node head;
private int numItems; // number of items in list
public ListReferenceBased() {
numItems = 0;
head = null;
} // end default constructor
public boolean isEmpty() {
return numItems == 0;
} // end isEmpty
public int size() {
return numItems;
} // end size
ADT List continued: Node
private Node find(int index) {
// -------------------------------------------------// Locates a specified node in a linked list.
// Precondition: index is the number of the desired
// node. Assumes that 1 <= index <= numItems+1
// Postcondition: Returns a reference to the desired
// node.
// -------------------------------------------------Node curr = head;
for (int skip = 1; skip < index; skip++) {
curr = curr.getNext();
} // end for
return curr;
} // end find
ADT List continued: Object
public Object get(int index)
throws ListIndexOutOfBoundsException {
if (index >= 1 && index <= numItems) {
// get reference to node, then data in node
Node curr = find(index);
Object dataItem = curr.getItem();
return dataItem;
}
else {
throw new ListIndexOutOfBoundsException(
"List index out of bounds exception on
get");
} // end if
} // end get
ADT List continued: Add
public void add(int index, Object item)
throws ListIndexOutOfBoundsException {
if (index >= 1 && index <= numItems+1) {
if (index == 1) {
// insert the new node containing item at
// beginning of list
Node newNode = new Node(item, head);
head = newNode;
}
else {
Node prev = find(index-1);
// insert the new node containing item after
// the node that prev references
Node newNode = new Node(item, prev.getNext());
prev.setNext(newNode);
} // end if
numItems++;
}
else {
throw new ListIndexOutOfBoundsException(
"List index out of bounds exception on add");
} // end if
} // end add
ADT List continued: Remove
public void remove(int index)
throws ListIndexOutOfBoundsException {
if (index >= 1 && index <= numItems) {
if (index == 1) {
// delete the first node from the list
head = head.getNext();
}
else {
Node prev = find(index-1);
// delete the node after the node that prev
// references, save reference to node
Node curr = prev.getNext();
prev.setNext(curr.getNext());
} // end if
numItems--;
} // end if
else {
throw new ListIndexOutOfBoundsException(
"List index out of bounds exception on
remove");
} // end if
}
// end remove
ADT List continued: RemoveAll
public void removeAll() {
// setting head to null causes list to be
// unreachable and thus marked for garbage
// collection
head = null;
numItems = 0;
} // end removeAll
} // end ListReferenceBased
To be added
• Recursive processing
• Comparing array-based and reference-based
implementations
• Tail references
• Dummy head nodes
• Circular lists
• Doubly linked lists
• More examples covered in class