241-423 Advanced Data Structures and Algorithms Semester 2, 2013-2014 5. Linked Lists • Objective – implement and use linked lists 241-423 ADSA: Linked Lists/5 1 Contents 1. An ArrayList can be Slow 2. What is a Linked List? 3. Java Assignment Differences 4. Implementing a Linked List 5. Using a Linked List 6. Doubly Linked Lists 7. The (Doubly) LinkedList Collection 8. Palindromes 241-423 ADSA: Linked Lists/5 2 1. An ArrayList can be Slow • Inserting/removing an element inside an ArrayList requires data shifting – O(n) operations 241-423 ADSA: Linked Lists/5 3 2. What is a Linked List? • Each element (node) inside a linked list is linked to the previous node and successor (next) node. • This allows for more efficient insertion and deletion of nodes. Why? 5 241-423 ADSA: Linked Lists/5 3 14 2 continued 4 • Inserting a new node only involves breaking one link, and linking the list to both ends of the new node: – all are O(1) operations 241-423 ADSA: Linked Lists/5 continued 5 • Removal of a node only requires the breaking of its two links, removal of the node, and then the relinking of the list: – all are O(1) operations 241-423 ADSA: Linked Lists/5 continued 6 • The insertion/removal of a node is a local operation – only the links next to the node need to be changed – the other nodes in the list are not affected – fast: O(1) • An ArrayList must shift lots of elements when an element is inserted/removed – slow: O(n) 241-423 ADSA: Linked Lists/5 7 3. Java Assignment Differences Foo a = new Foo(); Foo b; b = a; a b copy the link (the reference) Foo object 32 a 241-423 ADSA: Linked Lists/5 int a = 32; int b; b = a; copy the value 32 b 8 4. Implementing a Linked List • Each node is an object containing a value and a link (reference) to the next node (object) in the list – a singly-linked list • The list uses a 'front' variable to point to the first object in the list. • The reference in the last object is null. 241-423 ADSA: Linked Lists/5 9 Accessing a Node • Nodes in a singly-linked list are accessed by moving forward one node at a time from the front – called sequential access – a linked list is not a direct access structure like an array – this means that access is slower than in an array • • linked list access (if index is known): O(n) array list access (if index is known): O(1) 241-423 ADSA: Linked Lists/5 10 Nodes in a Linked List • Each Node object contains two variables: – nodeValue, of generic type T – next, a reference that links to the next node 241-423 ADSA: Linked Lists/5 11 The Node Class public class Node<T> { public T nodeValue; public Node<T> next; // data held by the node // next node in the list public Node() { nodeValue = null; next = null; } public Node(T item) { nodeValue = item; next = null; } } 241-423 ADSA: Linked Lists/5 12 • The variables in the Node class are public to simplify the coding using linked lists – bad style (from Ford & Topp, not me ☺) • The Node class is self-referencing: – next refers to (points to) an object of the same type 241-423 ADSA: Linked Lists/5 13 Creating a Linked List // create two nodes (figure (a) Node<String> p = new Node<String>("red"); Node<String> q = new Node<String>("green") // link p to q p.next = q; // figure (b) // set front to point at the first node Node<String> front = p; // figure (c) green red p q (a) Create nodes p and q 241-423 ADSA: Linked Lists/5 red green p (b) Link p to q q front red green p q (c) Assign front to point at p (red) continued 14 • If the linked list is empty, front is assigned null. 241-423 ADSA: Linked Lists/5 15 Scanning a Linked List • We scan a singly linked list by starting at the front, and then move along the list one Node at a time – sequential access (O(n)) – stop when we reach null • toString() is an example of a scanning method, which builds a string as it moves along the list. 241-423 ADSA: Linked Lists/5 16 toString() public static <T> String toString(Node<T> front) // build a string from the list of the form // "[ n1, n2, ..., nx ]" { if (front == null) // empty list return "[]"; Node<T> curr = front; // start at the front String s = "[" + curr.nodeValue; while(curr.next != null) { curr = curr.next; // move along list s += ", " + curr.nodeValue; } s += "]"; return s; } 241-423 ADSA: Linked Lists/5 17 Moving to a List Position • To move to an element at position x, we need to start at the front and move through the list counting up to x – sequential access again (O(n)) • The first element of the list is at position 0. 241-423 ADSA: Linked Lists/5 continued 18 Node<T> curr = front; // start at the front of list for (int i = 0; i < xPos; i++) curr = curr.next; // move along list pos 0 pos 1 pos 2 front pos 3 pos 3 front curr curr Assign curr to front 241-423 ADSA: Linked Lists/5 Move curr with 3 iterations 19 Updating the Front of the List • Inserting or deleting an element at the front of a list is easy (and fast) because the 'front' variable always points to the first element: – the operations are O(1) 241-423 ADSA: Linked Lists/5 20 Insert at the Front Node<T> newNode = new Node<T>(item); // insert item at the front of the list newNode.next = front; front = newNode; // front item newNode 241-423 ADSA: Linked Lists/5 21 Delete from the Front front = front.next; // move front to next node // front front.next 241-423 ADSA: Linked Lists/5 22 General Insertion • To insert a new node before a node referenced by 'curr', the code must have access to the previous node, 'prev', since its link must be changed. 241-423 ADSA: Linked Lists/5 continued 23 Node<T> curr = Node<T> prev = ... // set to point to a node ... // set to point to previous node Node<T> newNode = new Node<T>(item); // update links newNode.next = curr; prev.next = newNode; 241-423 ADSA: Linked Lists/5 // new node // step 1 // step 2 continued 24 • The insertion is O(1) since only two links need to be changed. But the real cost is the sequential search to find the insertion position, which is O(n). 241-423 ADSA: Linked Lists/5 25 General Deletion • Deleting a node at position curr requires access to the predecessor node prev. Node<T> curr = Node<T> prev = ... // set to point to a node ... // set to point to previous node // connect prev to curr.next prev.next = curr.next; curr.next = null; 241-423 ADSA: Linked Lists/5 continued 26 • The deletion is O(1) since only two links need to be changed. But the real cost is the sequential search to find the deletion position, which is O(n) – this is shown in the remove() method, which is explained next 241-423 ADSA: Linked Lists/5 27 Removing a Target Node • To remove the first node having a specified value, scan the list to find the node. • The scan must use two references that move together down the list – one reference (curr) points to the current node in the scan – the other reference (prev) points to the previous node 241-423 ADSA: Linked Lists/5 continued 28 • Once 'curr' finds the node, the code uses 'prev' to unlink 'curr'. 241-423 ADSA: Linked Lists/5 continued 29 • At the start, point 'curr' at the front of the list and set 'prev' to null, since the first node does not have a predecessor. • Move 'curr' and 'prev' down the list until curr.nodeValue matches the target or curr == null. 241-423 ADSA: Linked Lists/5 continued 30 • If the target is found then 'curr' points at the node and 'prev' to the predecessor node. • But there are two possible cases: – the target node is the first node, so 'prev' is null – the target node is not the first node, so 'prev' points to something 241-423 ADSA: Linked Lists/5 continued 31 • Case 1: 'prev' is null which means that 'curr' points to the first node. – so we only have to delete the front of the list front = curr.next; curr.next = null; 241-423 ADSA: Linked Lists/5 32 • Case 2: The match occurs in the middle of the list. Both 'curr' and 'prev' have non-null values. Unlink the current node. prev.next = curr.next; curr.next = null; 241-423 ADSA: Linked Lists/5 continued 33 • The generic remove() is passed a reference to the front of the list and the target value. • The method returns the value of 'front', which may have been updated if the first node was deleted. 241-423 ADSA: Linked Lists/5 34 remove() Method public static <T> Node<T> remove(Node<T> front, T target) /* Delete the first occurrence of the target in the linked list referenced by front; return the value of front */ { // initialize pointers Node<T> curr = front; Node<T> prev = null; boolean foundItem = false; // set to true if we find the target : 241-423 ADSA: Linked Lists/5 35 } // scan until find item or end of list (O(n)) while (curr != null && !foundItem) { // check for a match if (target.equals(curr.nodeValue)) { if (prev == null) // remove first Node (O(1)) front = front.next; else // erase middle Node (O(1)) prev.next = curr.next; curr.next = null; foundItem = true; } else { // advance curr and prev prev = curr; curr = curr.next; } } return front; // may be updated // end of remove() 241-423 ADSA: Linked Lists/5 36 5. Using a Linked List import java.util.Random; import java.util.Scanner; import ds.util.Node; import ds.util.Nodes; // methods using Node<T> public class ListExample { public static void main(String[] args) { // the initial list is empty Node<Integer> front = null; Random rnd = new Random(); Scanner keyIn = new Scanner(System.in); : 241-423 ADSA: Linked Lists/5 37 System.out.print("Enter the size of the list: "); int listCount = keyIn.nextInt(); // create a list Node<Integer> newNode; for (int i = 0; i < listCount; i++) { newNode = new Node<Integer>(rnd.nextInt(100)); newNode.next = front; // insert at list front front = newNode; } System.out.print("Original list: "); System.out.println( Nodes.toString(front) ); : 241-423 ADSA: Linked Lists/5 38 System.out.print("Ordered list: } "); Node<Integer> p; while (front != null) { // list not empty p = getMaxNode(front); // get largest System.out.print(p.nodeValue + " "); front = Nodes.remove(front, p.nodeValue); } System.out.println(); // end of main() 241-423 ADSA: Linked Lists/5 39 public static <T extends Comparable<? super T>> Node<T> getMaxNode(Node<T> front) { Node<T> maxNode = front; // initial values Node<T> curr = front.next; T maxValue = front.nodeValue; while (curr != null) { // try to update maxNode and maxValue if (maxValue.compareTo(curr.nodeValue)< 0) { maxValue = curr.nodeValue; maxNode = curr; } curr = curr.next; } return maxNode; } // end of getMaxNode() } // end of ListExample class 241-423 ADSA: Linked Lists/5 40 Execution 241-423 ADSA: Linked Lists/5 41 6. Doubly Linked Lists • A node in a doubly-linked list contain two references that point to the next node and the previous node. • front points to the first node in the list • back points at the last node in the list 241-423 ADSA: Linked Lists/5 continued 42 • A doubly-linked list can be scanned in both directions: – a forward scan starts at 'front' and ends when the link is to the same object as 'back' – a backward scan starts at 'back' and ends when the link is to the same object as 'front' 241-423 ADSA: Linked Lists/5 continued 43 • Like a singly-linked list, a doubly linked list is a sequential structure. • To move forward or backward, use the node links 'next' and 'prev'. • Unlike a singly linked list, the insert and delete operations only need a single reference to the node. 241-423 ADSA: Linked Lists/5 continued 44 • Insertion into a doubly linked list requires four reference assignments. prevNode = curr.prev; newNode.prev = prevNode; prevNode.next = newNode; curr.prev = newNode; newNode.next = curr; 241-423 ADSA: Linked Lists/5 // // // // 1 2 3 4 continued 45 • To delete a node curr, link the predecessor (curr.prev) of 'curr' to the successor of 'curr' (curr.next). prevNode = curr.prev; succNode = curr.next; succNode.prev = prevNode; prevNode.next = succNode; curr.prev = null; curr.next = null; 241-423 ADSA: Linked Lists/5 // 1 // 2 continued 46 • In a singly-linked list, adding and removing a node at the front of the list are O(1) operations. • With a doubly linked list, you can add and remove a node at the back of the list with the same O(1) efficiency. 241-423 ADSA: Linked Lists/5 47 7. The (Doubly) LinkedList Collection In Ford & Topp's DSA package 241-423 ADSA: Linked Lists/5 48 UML for LinkedList 241-423 ADSA: Linked Lists/5 49 LinkedList Methods • The LinkedList() constructor creates an empty list. • The toString() method returns a string representing the list as a comma-separated sequence of elements enclosed in brackets. 241-423 ADSA: Linked Lists/5 continued 50 • Reuse the Collection methods: – isEmpty(), size(), contains(), toArray() • add() inserts a new element at the back of the list and returns true. • remove() with an Object reference deletes the first occurrence of the object in the list – the method returns true or false depending on whether a match was found 241-423 ADSA: Linked Lists/5 51 LinkedList Examples LinkedList<String> aList = new LinkedList<String>(); alist.add("Red"); alist.add("Green"); alist.add("Blue); System.out.println("Size = " + aList.size()); System.out.println("List contains the string 'White' is " + aList.contains("White"); Size = 3 List contains the string 'White' is false 241-423 ADSA: Linked Lists/5 52 aList.add("Black"); aList.add("Blue"); // add Black at the end // add Blue at the end aList.remove("Blue"); // delete first "Blue" System.out.println(aList); // uses toString() [Red, Green, Black, Blue] 241-423 ADSA: Linked Lists/5 53 LinkedList Index Methods • The LinkedList can access and update an element with get() and set(), and modify the list with the add() and remove(). • The index methods have O(n) worst case running time. Use these methods only for small data sets. 241-423 ADSA: Linked Lists/5 54 Example // create list containing [5,7,9,4,3] Integer i= list.get(1); // i has value 7 list.remove(1); 241-423 ADSA: Linked Lists/5 // remove value at position 1 continued 55 list.set(2, 8); // store node 8 at position 2 list.add(2, 6); // store value 6 at position 2 241-423 ADSA: Linked Lists/5 56 Accessing the Ends of a LinkedList • Methods for the front of the list: – getFirst(), addFirst(), removeFirst() • For the back of the list: – getLast(), addLast(), removeLast() • They all are O(1) operations 241-423 ADSA: Linked Lists/5 57 End-of-List Examples LinkedList<String> list = new LinkedList<String>(); list.addFirst("Tom"); list.addFirst("Debbie"); list.addLast("David"); ist.addLast("Maria"); 241-423 ADSA: Linked Lists/5 continued 58 // identify the elements at the ends of the list System.out.println("First element is " + list.getFirst()); System.out.println("Last element is " + list.getLast()); First element is Debbie Last element is Maria 241-423 ADSA: Linked Lists/5 continued 59 // Exchange the first and last elements in the list. // remove elements at the ends of the list String firstElem = aList.removeFirst(); String lastElem = aList.removeLast(); // add elements back in switched positions aList.addLast(firstElem); aList.addFirst(lastElem); 241-423 ADSA: Linked Lists/5 continued 60 // Output elements in the list by position. // Repeatedly delete first element and display its // value until list is empty while (!aList.isEmpty()) System.out.print(aList.removeFirst() + " Maria Tom 241-423 ADSA: Linked Lists/5 David "); Debbie 61 Linked List as a Queue • A linked list is a natural way to implement a queue. – the element at the front of the queue can be removed with getFirst() – a new element can be added to the back of the queue by using addLast() 241-423 ADSA: Linked Lists/5 62 8. Palindromes • A palindrome is a string that reads the same forward and backward: – e.g. "level", "noon.", "Stack Cats" – ignore non-letters and let capitals == lowercase • isPalindrome() takes a LinkedList object as an argument and returns true if the sequence is a palindrome; false otherwise. 241-423 ADSA: Linked Lists/5 continued 63 Panic in a Titanic, I nap. Yawn a more Roman way. A Toyota's a Toyota Race car 241-423 ADSA: Linked Lists/5 64 public static boolean isPalindrome(LinkedList<?> aList) { // list must have 2 or more elements while (aList.size() > 1) { // compare elements on opposite ends of list if ( !aList.getFirst().equals(aList.getLast()) ) return false; // delete the matching elements aList.removeFirst(); aList.removeLast(); } // if we get here then list is a palindrome return true; } 241-423 ADSA: Linked Lists/5 65 The "?" Wildcard • isPalindrome() does not refer to the generic type of the list. In this case, we may use: LinkedList<?> aList • "?" means that we don't care about the type of the elements of the list. 241-423 ADSA: Linked Lists/5 66 Checking for a Palindrome import java.util.Scanner; import ds.util.LinkedList; public class CheckPali { public static void main(String[] args) { LinkedList<Character> charList = new LinkedList<Character>(); // get input line from user System.out.print("Enter a string: "); Scanner keyIn = new Scanner(System.in); String str = keyIn.nextLine(); : 241-423 ADSA: Linked Lists/5 67 // put all letters into list as lowercase chars for (int i = 0; i < str.length(); i++) { char ch = str.charAt(i); if (Character.isLetter(ch)) charList.addLast( Character.toLowerCase(ch) ); } } if (isPalindrome(charList)) System.out.println("'" + str + "' is a palindrome"); else System.out.println("'" + str + "' is not a palindrome"); // end of main() // isPalindrome() method goes here } // end of CheckPali class 241-423 ADSA: Linked Lists/5 68 Execution 241-423 ADSA: Linked Lists/5 69