Survey
* Your assessment is very important for improving the workof artificial intelligence, which forms the content of this project
* Your assessment is very important for improving the workof artificial intelligence, which forms the content of this project
Linked Lists Dr. Tim Margush University of Akron © 2009 Goals • Describe a linked data structure • Perform basic operations on a linked data structure using a Node class • Implement the List ADT using a linked data structure • Compare the advantages of linked to array-based implementations of a list © 2009 Dr. Tim Margush Backing Store • Every List implementation requires a backing store – A data structure containing elements of the List • An array is one possible backing store – Arrays store data references physically contiguous memory locations • A linked structure is another – References to data in linked structures are scattered throughout memory © 2009 Dr. Tim Margush Array vs. Linked Structure • References to List elements can be stored – In physical sequential order (array) – In random locations (linked) • Must be able to locate the first and next items – list[0], list[1], … – head, current.next • The first element is special • Each element "knows" where the next is © 2009 Dr. Tim Margush Links • Links specify the location of something • Links can be integers, or references – Object t; • t is a link (reference) to an Object – int loc; • loc is an array subscript used to locate an Object (or primitive value) © 2009 Dr. Tim Margush Nodes • Nodes support linked structures by bundling data with one or more links to other elements of the collection • A linked list uses Nodes with a data value and one or more links (references) to other Nodes class Node<E>{ private E data; private Node<E> next; } © 2009 Dr. Tim Margush Inner Classes • An inner class is defined inside another class • A private inner class is only accessible by the outer (enclosing) class public class LList<E> implements ListInterface<E>{ private class Node{ private E data; private Node next; } private Node head; © 2009 Dr. Tim Margush Inner Classes • Methods in both the inner and outer class have complete access to all members of each other, including private members public class LList<E> implements ListInterface<E>{ private class Node{ private Node(){ //access private outer member if (head == null) … } } //access private constructor private Node head = new Node(); © 2009 Dr. Tim Margush Visualization of a Linked List null Node head; © 2009 Dr. Tim Margush Inner Class: Node public class LList<E> implements ListInterface<E>{ private class Node{ private E data; private Node next; private Node(E newEntry){ this(newEntry, null); } private Node(E newEntry, Node n){ data = newEntry; next = n; } } } … © 2009 Dr. Tim Margush Linked List Implementation public class LList<E> implements ListInterface<E>{ private Node head; private int size; //insert inner class for Node here public LList(){ clear(); } public final void clear(){ head = null; size = 0; } … } © 2009 Dr. Tim Margush Linked List add public boolean add(int newPosition, E newEntry){ if (newPosition<0 || newPosition>size) return false; Node prev = getNodeAt(newPosition-1); if (prev == null){ head = }else{ prev.next = } size++; return true; } © 2009 Dr. Tim Margush getNodeAt, add private Node getNodeAt(int loc){ Node temp = head; if (loc<0 || loc>=size) return null; while(loc-- > 1) temp = return temp; } public boolean add(E newEntry){ return } © 2009 Dr. Tim Margush remove public E remove(int position){ if (position<0 || position>-size) return null; Node todelete, prev = getNodeAt(position-1); if (prev == null){ todelete = head; head = todelete.next; }else{ } size--; return todelete.data; } © 2009 Dr. Tim Margush Array vs. Linked List Backing Store Array • Wastes memory in unused positions • Resizing requires moving many references • Random access is efficient • Insert in middle requires shifting • Add to end is fast • Add to beginning is costly Linked list • Uses only the memory needed for the data actually in the list • Resizing is never necessary • Random access is inefficient • Insert in middle is efficient after locating previous • Add to end is costly • Add to beginning is fast © 2009 Dr. Tim Margush Variations on Node • class Node should be accessible only to LList (or other collection-type clients) – Private inner class • Add public get/set methods – Package access • Access can be package or public • Set/get methods are optional as free access is available inside package (unless declared private) © 2009 Dr. Tim Margush Package Model package ListPackage; //Everything in this class is package access Class Node<E>{ E data; Node<E> next; Node(E newEntry){ this(newEntry, null); } Node(E newEntry, Node<E> n){ data = newEntry; next = n; } } © 2009 Dr. Tim Margush Package Model - add package ListPackage; public class LList<E>{ Node<E> head; int size; public boolean add(E newEntry){ if (head==null) head = new Node<E>(newEntry); else{ //note package access requirement getNodeAt(size).next = new Node<E>(newEntry); size++; return true; } … } © 2009 Dr. Tim Margush Package Model - getEntry package ListPackage; public class LList<E>{ //illustrates package access to member public boolean getEntry(int position){ if (position>=0 && position<size) return getNodeAt(position).data; else return null; } } © 2009 Dr. Tim Margush Circular Lists Head and Tail Access • List keeps reference to last Node – Facilitates add to end since we always know where the last node is • Last Node points to first node – Otherwise, how would we know where the first Node was located? • This can also be done with a standard list maintaining a head and tail pointer © 2009 Dr. Tim Margush Small Lists 0 a 1 b a 2 © 2009 Dr. Tim Margush Circular Traverse public void display(){ if (last==null) return; Node current = last; do{ current = current.next; System.out.println(current.data); while(current!=last); } a b 3 © 2009 Dr. Tim Margush c getNodeAt private Node getNodeAt(int position){ //node at 0 or size is the last node (or null if empty) if (position<0||position>size) return last; Node current = last.next; while(position-- > 0) current = current.next; return current; } a b 3 © 2009 Dr. Tim Margush c Circular Add • Add to end is to add between the last and first – Insert between last and first • last = last.next = new Node(x, last.next); • If empty, last.next is illegal – New node is last and points to self • last.next = last = new Node(x); • Adding at the front is even easier – Assuming not empty • last.next = new Node(x, last.next); © 2009 Dr. Tim Margush Circular - add public boolean add(E newEntry){ if (last==null) last.next = last = new Node(newEntry); else last = last.next = new Node(newEntry, last.next); size++; } public boolean add(int newLoc, E newEntry){ if (newLoc==size) //add to end case return add(newEntry); //adding somewhere other than at the end Node prev = getNodeAt(newLoc-1); prev.next = new Node(newEntry, prev.next); size++; } © 2009 Dr. Tim Margush Circular - remove public E remove(int loc){ if (loc<0 || loc>=size) return null; Node prev = getNodeAt(loc-1); Node toRemove = prev.next; prev.next = toRemove.next; if (loc==size-1) last = prev; size--; if (size==0) last = null; return toRemove.data; } © 2009 Dr. Tim Margush Dummy Header Node • First Node always exists as a placeholder, but is not used to store data – Node head = new Node(null); • Eliminates special case of no previous node – getNodeAt(-1) returns the header Node. © 2009 Dr. Tim Margush Doubly-Linked • Each Node has a forward and backward link • Usually combined with circular list structure with dummy header private Node<E>{ private E data; private Node<E> prev, next; private Node(E newEntry){ data = newEntry; prev = next = this; //circular } private Node(E newEntry, Node<E> p, Node<E> n){ data = newEntry; prev = p; next = n; } } © 2009 Dr. Tim Margush Doubly-Linked - add class DLCList<E> implements ListInterface<E>{ private Node head = new Node(null); //size and private Node class here public boolean add(newEntry){ Node last = head.prev; last.next = head.prev = new Node(newEntry,last,head); size++; return true; } public boolean add(int newLoc, E newEntry){ Node prev = getNodeAt(newLoc-1); Node after = prev.next; prev.next = after.prev = new Node(newEntry,prev,after); size++; return true; } © 2009 Dr. Tim Margush Doubly-Linked - remove • No special cases! public E remove(int loc){ Node toRemove = getNodeAt(loc); toRemove.prev.next = toRemove.next; toRemove.next.prev = toRemove.prev; size--; return toRemove.data; } © 2009 Dr. Tim Margush Doubly-Linked - getNodeAt • Efficiently scan for location private Node getNodeAt(int loc){ if(loc<-1 || loc>=size) return null; Node current = head; //location 0 or size+1 if(loc<size/2){ //scan forward int pos=0; while(pos++ < loc) current = current.next; }else{ //scan in reverse int pos = size+1; do current = current.prev; while(--pos > loc); } return current; } © 2009 Dr. Tim Margush Linked List • Doubly-Linked List – Provides for forward or backward iteration – No linked list supports random access • getNodeAt is a non-trivial method • Dummy header and circularity – Eliminates special cases and all null references at the expense of one extra Node © 2009 Dr. Tim Margush java.util.LinkedList • Implements List, Cloneable, Serializable • Uses a doubly-linked list backing store • Includes special methods that take advantage of linked list structure – addFirst, addLast – getFirst, getLast – removeFirst, removeLast © 2009 Dr. Tim Margush