240 likes | 436 Views
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
E N D
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 = new MyNumber(9); MyNumber y = new MyNumber(9); MyNumber z = x; x == y; // ? 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.
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 Your Basic Generic 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 mustmodify 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