Lecture 23: Doubly Linked List CSE 116/504 – Intro. To Computer Science for Majors II Lecture 23: Doubly Linked List
"Convince your neighbors" Announcements Lectures have typical pattern for adult learning Ask question (ungraded) "Convince your neighbors" Ask question (graded) May see input dialog in TopHat but no question on screen Ignore these questions; testing students paying attention Lose points for answering; me having some "fun" Public domain picture of the creepy clown. Original available at: https://pixabay.com/p-2836388/?no_redirect
What Can We Know About List? List interface defined in java.util package Specifies all of the methods we can call on a List Effect of each method included in these definition Also includes value returned & potential exceptions Start TopHat slide –do NOT show to class
List ADT Based upon a imagined resizable array Arbitrary sequence of elements stored within list List ADT keeps elements indexed from 0 to n-1 Index just int; gives absolute position in List 1 2 3 4 5
Linked List Nodes ("thingys") very limited in functionality Design splits Entry from List implementation Excellent OO design: each class focused on 1 job LinkedList head size 4 modCount 99 Advance TopHat slide – still do NOT show to class Entry Entry Entry Entry elem next elem next elem next elem next
1st Key Point for Linked Lists Linked list chain of Entrys Entry is linked list
2nd Key Point for Linked Lists Entry holds element Entry is element
List Methods boolean add(E e); void add(int i, E e); E set(int i, E e); boolean remove(Object o); E remove(int i); boolean contains(Object o); int indexOf(Object o); E get(int i); // size(),isEmpty(),iterator() & other boring ones
Linked lists use Entrys but List Method Issue Linked lists use Entrys but List interface specifies int boolean add(E e); void add(int i, E e); E set(int i, E e); boolean remove(Object o); E remove(int i); boolean contains(Object o); int indexOf(Object o); E get(int i); // size(),isEmpty(),iterator() & other boring ones
3rd Key Point for Linked Lists LinkedList is-a List Traverses Entrys to reach target
Fill in the Blank trav.getNext(); trav += 1; trav.getElement(); Entry<E> trav = head while (trav != null && !/* found node */) /* Update vars helping find node */ ___________________ end while trav.getNext(); trav += 1; trav.getElement(); trav = trav.getNext(); 1
Convince neighbor your answer Fill in the Blank Entry<E> trav = head while (trav != null && !/* found node */) /* Update vars helping find node */ ___________________ end while Convince neighbor your answer is correct trav.getNext(); trav += 1; trav.getElement(); trav = trav.getNext();
Fill in the Blank trav.getNext(); trav += 1; trav.getElement(); Entry<E> trav = head while (trav != null && !/* found node */) /* Update vars helping find node */ ___________________ end while trav.getNext(); trav += 1; trav.getElement(); trav = trav.getNext(); 1
Entry IS NOT index Fill in the Blank trav.getNext(); trav += 1; Entry<E> trav = head while (trav != null && !/* found node */) /* Update vars helping find node */ ___________________ end while Entry IS NOT index trav.getNext(); trav += 1; trav.getElement(); trav = trav.getNext();
Entry IS NOT element Fill in the Blank trav.getNext(); trav += 1; Entry<E> trav = head while (trav != null && !/* found node */) /* Update vars helping find node */ ___________________ end while trav.getNext(); trav += 1; trav.getElement(); trav = trav.getNext(); Entry IS NOT element
Closer, but what happens to return value? Fill in the Blank Entry<E> trav = head while (trav != null && !/* found node */) /* Update vars helping find node */ ___________________ end while Closer, but what happens to return value? trav.getNext(); trav += 1; trav.getElement(); trav = trav.getNext();
trav never assigned to new alias! Fill in the Blank Entry<E> trav = head while (trav != null && !/* found node */) /* Update vars helping find node */ ___________________ end while Closer, but what happens to return value? trav never assigned to new alias! trav.getNext(); trav += 1; trav.getElement(); trav = trav.getNext();
trav never assigned to new alias! Fill in the Blank Entry<E> trav = head while (trav != null && !/* found node */) /* Update vars helping find node */ ___________________ end while trav never assigned to new alias! Return value goes: trav.getNext(); trav += 1; trav.getElement(); trav = trav.getNext();
Fill in the Blank trav.getNext(); trav += 1; trav.getElement(); Entry<E> trav = head while (trav != null && !/* found node */) /* Update vars helping find node */ ___________________ end while trav.getNext(); trav += 1; trav.getElement(); trav = trav.getNext(); Gets next Entry in list
AND reassigns trav so it aliases next Node Fill in the Blank Entry<E> trav = head while (trav != null && !/* found node */) /* Update vars helping find node */ ___________________ end while trav.getNext(); trav += 1; trav.getElement(); trav = trav.getNext(); Gets next Entry in list AND reassigns trav so it aliases next Node
Traversal Algorithm Starts List method needing non-head nodes Entry<E> trav = head while (trav != null && ! /* found node */ ) /* Update vars helping find node */ trav = trav.getNext() end while /* Now use trav to complete method */
Traversal's Big-Oh Complexity? Entry<E> trav = head while (trav != null && !/*found node*/) /* Update vars helping find node */ trav = trav.getNext() end while Impossible to know without the number of nodes in linked list O(n) Depends on where the needed node is O(1) 1
Traversal's Big-Oh Complexity? Entry<E> trav = head while (trav != null && !/*found node*/) /* Update vars helping find node */ trav = trav.getNext() end while Convince neighbor your answer is correct Impossible to know without the number of nodes in linked list O(n) Depends on where the needed node is O(1)
Traversal's Big-Oh Complexity? Entry<E> trav = head while (trav != null && !/*found node*/) /* Update vars helping find node */ trav = trav.getNext() end while Impossible to know without the number of nodes in linked list O(n) Depends on where the needed node is O(1) 1
Traversal's Big-Oh Complexity? Entry<E> trav = head while (trav != null && !/*found node*/) /* Update vars helping find node */ trav = trav.getNext() end while Big-Oh based on input size; linked list is input, so number of nodes = n Impossible to know without the number of nodes in linked list O(n) Depends on where the needed node is O(1)
Traversal's Big-Oh Complexity? Entry<E> trav = head while (trav != null && !/*found node*/) /* Update vars helping find node */ trav = trav.getNext() end while Impossible to know without the number of nodes in linked list O(n) Depends on where the needed node is O(1) Big-Oh measures worst case; assume node not found
Traversal's Big-Oh Complexity? Entry<E> trav = head while (trav != null && !/*found node*/) /* Update vars helping find node */ trav = trav.getNext() end while Impossible to know without the number of nodes in linked list O(n) Depends on where the needed node is O(1) Does constant work
Traversal's Big-Oh Complexity? Entry<E> trav = head while (trav != null && !/*found node*/) /* Update vars helping find node */ trav = trav.getNext() end while Impossible to know without the number of nodes in linked list O(n) Depends on where the needed node is O(1) Does constant work for every node
Traversal's Big-Oh Complexity? Entry<E> trav = head while (trav != null && !/*found node*/) /* Update vars helping find node */ trav = trav.getNext() end while Impossible to know without the number of nodes in linked list O(n) Depends on where the needed node is O(1) Does constant work for every node in worst case
ArrayList v. LinkedList Method ArrayList LinkedList w/ tail remove(i) O(n) (generally) O(1) (if i=0) remove(e) O(n) contains(e) indexOf(e) get(i)/ set(i,e) O(1) (at either end) add(e) O(1) add(i,e)
Array-based List.remove(i) remove(i) "shifts" elements down to fill hole O(n) time required in the general case If i =size()-1, have special best case of O(1) time But must consider worst case when computing big-Oh 1 2 e n-1 i
Array-based List.add(i, e) add(i, e) "shifts" elements to make space in array When array fills, must allocate new array & copy over Time needed based on i – amount getting moved If adding to end, have best case of O(1) time O(n) general/worst case from shifting all elements 1 2 n-1 i
Array-based List.add(i, e) add(i, e) "shifts" elements to make space in array When array fills, must allocate new array & copy over Time needed based on i – amount getting moved If adding to end, have best case of O(n) time O(n) general/worst case from shifting all elements 1 2 n-1 i
Other List’s Methods remove() removes element IF in List already Requires comparing with elements currently in List contains() checks if element already included indexOf() returns first index or -1 if not in list get()/set() uses element at specific index No comparison required, just requires using array index
ArrayList v. LinkedList Method ArrayList LinkedList w/ tail remove(i) O(n) (generally) O(1) (if i=size-1) O(1) (if i=0) remove(e) O(n) contains(e) indexOf(e) get(i)/ set(i,e) O(1) O(1) (at either end) add(e) add(i,e)
ArrayList v. LinkedList Method ArrayList LinkedList w/ tail remove(i) O(n) (generally) O(1) (if i=size-1) O(1) (if i=0) remove(e) O(n) contains(e) indexOf(e) get(i)/ set(i,e) O(1) O(1) (at either end) add(e) add(i,e) O(1) (at either end)
Problem with Singly Linked tail improves performance at List's end add(e) like add(0,e) but uses tail rather head When head or tail used, get() & set() very quick remove() with tail slow; traverse to find prior node LinkedList head tail size 4 Entry Entry Entry Entry elem next elem next elem next elem next
Using Singly Linked List Nodes added & removed along with elements Unlike array-based List, limits demands on memory 1st and last element quick to add, retrieve, & update O(1) time makes linked list fastest removing 1st element Slowest removing last element needing full O(n) time
Using Singly Linked List Nodes added & removed along with elements Unlike array-based List, limits demands on memory 1st and last element quick to add, retrieve, & update O(1) time makes linked list fastest removing 1st element Slowest removing last element needing full O(n) time Means linked lists great when end elements used But not equally, add either end but only remove head
Using Singly Linked List Nodes added & removed along with elements Unlike array-based List, limits demands on memory 1st and last element quick to add, retrieve, & update O(1) time makes linked list fastest removing 1st element Slowest removing last element needing full O(n) time Means linked lists great when end elements used But not equally, add either end but only remove head
Doubly Linked List Link to previous node in list also in each node Each doubly-linked node contains: Element (data) reference Link to next Entry Prev(ious) Entry also linked elem Doubly-linked node next prev
Doubly Linked List Link to previous node in list also in each node Each doubly-linked node contains: Element (data) reference Link to next Entry Prev(ious) Entry also linked
Implementing Entry Doubly-linked node could extend Entry next & elem fields exist in both of these classes Only difference is prev field added for doubly-linked Allows mixing singly- & doubly-linked in same list But this is weird and would almost never happen Prevent this monstrosity! Do not use inheritance here
1st Entry Class (Singly-Linked) public class Entry<T> { private T element; private Entry<T> next; public Entry(T e, Entry<T> n) { element = e; next = n; } public T getElement() { return element; } public Entry<T> getNext() { return next; } // Rest of the getters and setters omitted because they are boring }
2nd Entry Class (Doubly-Linked) public class Entry<Bob> { private Bob element; private Entry<Bob> next, prev; public Entry(Bob e, Entry<Bob> n, Entry<Bob> p) { element = e; next = n; prev = p; } public Bob getElement() { return element; } public Entry<Bob> getNext() { return next; } public void setPrev(Entry<Bob> newPrev) { prev = newPrev; } // Rest of the getters and setters omitted because they are boring }
2nd Entry Class (Doubly-Linked) public class Entry<Bob> { private Bob element; private Entry<Bob> next, prev; public Entry(Bob e, Entry<Bob> n, Entry<Bob> p) { element = e; next = n; prev = p; } public Bob getElement() { return element; } public Entry<Bob> getNext() { return next; } public void setPrev(Entry<Bob> newPrev) { prev = newPrev; } // Rest of the getters and setters omitted because they are boring }
2nd Entry Class (Doubly-Linked) public class Entry<T> { private T element; private Entry<T> next, prev; public Entry(T e, Entry<T> n, Entry<T> p) { element = e; next = n; prev = p; } public T getElement() { return element; } public Entry<T> getNext() { return next; } public void setPrev(Entry<T> newPrev) { prev = newPrev; } // Rest of the getters and setters omitted because they are boring }
Removing the Tail Use Entry's prev field to get next-to-last Entry Avoids the O(n) traversal using O(1) field lookup Just set tail field in LinkedList class like this: tail = tail.getPrev(); LinkedList head tail size 4 modCount 6
Removing the Tail Use Entry's prev field to get next-to-last Entry Avoids the O(n) traversal using O(1) field lookup Just set tail field in LinkedList class like this: tail = tail.getPrev(); LinkedList head tail size 4 modCount 6
Removing the Tail Use Entry's prev field to get next-to-last Entry Avoids the O(n) traversal using O(1) field lookup Just set tail field in LinkedList class like this: tail = tail.getPrev(); size -= 1; modCount += 1; LinkedList head tail size 3 modCount 7
Removing the Tail Use Entry's prev field to get next-to-last Entry Avoids the O(n) traversal using O(1) field lookup Just set tail field in LinkedList class like this: tail = tail.getPrev(); size -= 1; modCount += 1; LinkedList head tail size 3 modCount 7
Removing the Tail Use Entry's prev field to get next-to-last Entry Avoids the O(n) traversal using O(1) field lookup Just set tail field in LinkedList class like this: tail = tail.getPrev(); size -= 1; modCount += 1; LinkedList head tail size 3 modCount 7
Removing the Tail Use Entry's prev field to get next-to-last Entry Avoids the O(n) traversal using O(1) field lookup Just set tail field in LinkedList class like this: tail = tail.getPrev(); size -= 1; modCount += 1; tail.setNext(null); LinkedList head tail size 3 modCount 7
Removing the Tail Use Entry's prev field to get next-to-last Entry Avoids the O(n) traversal using O(1) field lookup Just set tail field in LinkedList class like this: tail = tail.getPrev(); size -= 1; modCount += 1; tail.setNext(null); LinkedList head tail size 3 modCount 7
Removing the Tail Use Entry's prev field to get next-to-last Entry Avoids the O(n) traversal using O(1) field lookup Just set tail field in LinkedList class like this: tail = tail.getPrev(); size -= 1; modCount += 1; tail.setNext(null); LinkedList head tail size 3 modCount 7
Removing an Internal Node Linked list's length equals number of elements Requires unlinking Entry from linked list Nothing fancy needed, just adjust prev & next links Entry 0nly included because of links, so this does it all
Removing an Internal Node Requires unlinking Entry from linked list Nothing fancy needed, just adjust prev & next links Entry 0nly included because of links, so this does it all LinkedList head tail size 3 modCount 3
Removing an Internal Node Requires unlinking Entry from linked list Nothing fancy needed, just adjust prev & next links Entry 0nly included because of links, so this does it all LinkedList head tail size 3 modCount 3
Removing an Internal Node Requires unlinking Entry from linked list Nothing fancy needed, just adjust prev & next links Entry 0nly included because of links, so this does it all LinkedList head tail size 3 modCount 3
Removing an Internal Node Requires unlinking Entry from linked list Nothing fancy needed, just adjust prev & next links Entry 0nly included because of links, so this does it all LinkedList head tail size 2 modCount 4
Removing Internal Node public class LinkedList<T> implements List<T> { private Entry<T> head; private Entry<T> tail; private int size; private long modCount; private void removeInternalNode(Entry<T> removeMe) { Entry<T> prior, follow;
Removing Internal Node public class LinkedList<T> implements List<T> { private Entry<T> head; private Entry<T> tail; private int size; private long modCount; private void removeInternalNode(Entry<T> removeMe) { Entry<T> prior, follow;
Removing Internal Node public class LinkedList<T> implements List<T> { /* More code exists, but removed for space */ private void removeInternalNode(Entry<T> removeMe) { Entry<T> prior, follow; LinkedList head tail size 2 modCount 4 prior
Removing Internal Node public class LinkedList<T> implements List<T> { /* More code exists, but removed for space */ private void removeInternalNode(Entry<T> removeMe) { Entry<T> prior, follow; prior = removeMe.getPrev(); LinkedList head tail size 2 modCount 4 follow prior
Removing Internal Node public class LinkedList<T> implements List<T> { /* More code exists, but removed for space */ private void removeInternalNode(Entry<T> removeMe) { Entry<T> prior, follow; prior = removeMe.getPrev(); follow = removeMe.getNext(); LinkedList head tail size 2 modCount 4 follow prior
Removing Internal Node public class LinkedList<T> implements List<T> { /* More code exists, but removed for space */ private void removeInternalNode(Entry<T> removeMe) { Entry<T> prior, follow; prior = removeMe.getPrev(); follow = removeMe.getNext(); prior.setNext( ); LinkedList head tail size 2 modCount 4 follow prior
Removing Internal Node public class LinkedList<T> implements List<T> { /* More code exists, but removed for space */ private void removeInternalNode(Entry<T> removeMe) { Entry<T> prior, follow; prior = removeMe.getPrev(); follow = removeMe.getNext(); prior.setNext(follow); follow.setPrev( ); LinkedList head tail size 2 modCount 4 follow prior
Removing Internal Node public class LinkedList<T> implements List<T> { /* More code exists, but removed for space */ private void removeInternalNode(Entry<T> removeMe) { Entry<T> prior, follow; prior = removeMe.getPrev(); follow = removeMe.getNext(); prior.setNext(follow); follow.setPrev(prior); size -= 1; modCount += 1; } LinkedList head tail size 2 modCount 4 follow prior
Coding is hard Drawing is easy Using drawings makes coding easier Programming Pro Tip Coding is hard Drawing is easy Using drawings makes coding easier
Almost Forgot Easy writing two of LinkedList boring methods size() -- returns size isEmpty() – returns if size is equal to 0 Easy start to third method since it has little work iterator() -- returns new LLIterator(); But now we must write LLIterator class
Almost Forgot Easy writing two of LinkedList boring methods size() -- returns size isEmpty() – returns if size is equal to 0 Easy start to third method since it has little work iterator() -- returns new LLIterator(); But now we must write LLIterator class
But Not That Much Work
Type of LLIterator's Cursor?
Best LLIterator Cursor Type? LLIterator lacks array, so no cursor is required T (the element type) Entry (the node type) int 18
Best LLIterator Cursor Type? LLIterator lacks array, so no cursor is required T (the element type) Entry (the node type) int Convince neighbor your answer is correct
Best LLIterator Cursor Type? LLIterator lacks array, so no cursor is required T (the element type) Entry (the node type) int 1
Best LLIterator Cursor Type? LLIterator lacks array, so no cursor is required T (the element type) Entry (the node type) int
Best LLIterator Cursor Type? LLIterator lacks array, so no cursor is required T (the element type) Entry (the node type) int Cursor used to access elements; cannot use element to access elements
Best LLIterator Cursor Type? LLIterator lacks array, so no cursor is required T (the element type) Entry (the node type) int
Best LLIterator Cursor Type? LLIterator lacks array, so no cursor is required T (the element type) Entry (the node type) int Cursor used to access elements; in a linked list, we use nodes to hold & access elements
Last Linked List Key Concept Entry in LLIterator equivalent to index in ArrayListIterator
Final LLIterator Details if cursor will access a valid location value at cursor’s location Advance cursor to refer to next location if (cursor != null) cursor.getElement() cursor.getNext()
Final LLIterator Details if cursor will access a valid location value at cursor’s location Advance cursor to refer to next location if (cursor != null) cursor.getElement() cursor.getNext()* * If you are not objecting, today's 1st TopHat failed
Final LLIterator Details if cursor will access a valid location value at cursor’s location Advance cursor to refer to next location if (cursor != null) cursor.getElement() cursor = cursor.getNext()
Does Absolute Position Matter?
Nodes Help With Relative Order
ArrayList v. LinkedList Method ArrayList LinkedList w/ tail remove(i,e) O(n) (generally) O(1) (if i=size-1) O(1) (if i=0) remove(e) O(n) contains(e) indexOf(e) get(i)/ set(i,e) O(1) O(1) (at either end) add(e) add(i,e) O(1) (at either end)
1st Key Point for Linked Lists Linked list chain of Entrys Entry is linked list
2nd Key Point for Linked Lists Entry holds element Entry is element
Linked lists use Entrys but List Method Issue Linked lists use Entrys but List interface specifies int boolean add(E e); void add(int i, E e); E set(int i, E e); boolean remove(Object o); E remove(int i); boolean contains(Object o); int indexOf(Object o); E get(int i); // size(),isEmpty(),iterator() & other boring ones
Last Linked List Key Concept Entry in LinkListIterator equivalent to index in ArrayListIterator
3rd Key Point for Linked Lists LinkedList is-a List Traverses Entrys to reach target
Traversal Algorithm Starts List method needing non-head nodes Entry<E> trav = head while (trav != null && ! /* found node */ ) /* Update vars helping find node */ trav = trav.getNext() end while /* Now use trav to complete method */
Coding is hard Drawing is easy Using drawings makes coding easier Programming Pro Tip Coding is hard Drawing is easy Using drawings makes coding easier
For Next Lecture Monday reviews material from midterm What are your questions from this midterm? What was your grade and can you see results? Were the Tim Hertz-tons questions from real world? Week #8 assignment available & due on Monday Can finish assignment now & relax this weekend! Phase #2 due in 10 days – make sure group is ready