CS201 - Spring 2014 - Class 19

  • exercises

  • changing variable names in Eclipse

  • Linked lists
       - linked lists are a recursive data structure
          - the data structure is built out of nodes
          - a node stores both a data item as well as a reference to another node
       - look at the IntNode class in IntLinkedList code

       - if someone gave us the first node, how would we traverse the linked list?
          IntNode finger = head;

          while( finger != null ){
             finger = finger.next();
          }

       - We want to build a linked list data structure that supports similar operations to ArrayList. How would we do it?
          - we'll just store a link the head of the linked list
          - all of our operations will start at the head and then modify/traverse the list appropriately

       - A few operations (look at IntLinkedList code)
          - public void clear()
             - just need to set head = null and we're done

          - A few more interesting ones to try:
             - public void remove(int value)
             - public int indexOf(int value)
             - public int get(int index)
             - public addLast(int value)

  • Generic linked lists
       - our linked list so far can only hold ints
       - how could we make it hold any type of value?
          - generics!
       - look at LinkedList class in LinkedList code

  • Which of these methods are fast/slow for singly linked lists?
       - add to the end (slow)
       - add to the front (fast)
       - contains (slow)
       - get (slow)
       - insert at an index
       - remove an element
       - set the value of an existing element
       - size

  • What does the linked list buy us?
       - add and remove at the front of the list in constant time!
       - what is the downside (no free lunch!)?
          - get and set are now linear time operations
          - size is linear time. Could we do better?
             - just keep another instance variable that is the number of elements   

  • Making it even better
       - Can you see any easy way to speed up any of these?
       - add just appends one thing on to the end of the list, it seems unfortunate that this takes linear time. How could we make it faster?
          - keep a tail reference
          - how does this help us?
             public add(E value){
                if( head == null ){
                   head = new Node<E>(value);
                   tail = head;
                }else{
                   tail.setNext(new Node<E>(value);
                   tail = tail.next();
                }
             }
          - what's the running time? Now it's constant!
       - what are the downsides to using a tail pointer?
          - more memory (though only marginally)
          - makes a few of the methods slightly more complicated
       - Can we get rid of it?
          - what would happen if we connected the next reference of the last element to the head of the list?

  • Circularly linked lists
       - In a circularly linked list, all nodes have a next reference creating one big loop
       - Do we still need both a head and a tail reference?
          - the head reference is just tail.next()
             - note that even when there is only one element, we'll still have a loop (with just one element) and therefore tail.next() will still reference the head of the list
       - Did we lose any functionality from before?
          - no, we can still add things and remove things to the head of the list in constant time
          - and add things to the tail of the list in constant time

  • Doubly linked list
       - We can remove items from the front of the list. What if we want to remove items from the end of the list?
          - ideally, you'd like to just splice it off
          - the problem, though, is that you need to know what the previous node is to do this, so you'd have to search through the list, which would still take linear time
       - the linked list we've seen so far is called a singly linked list since we just have a single forward link
       - in a doubly linked list our Node class has both a next reference as well as a prev reference
       - How does this help us?
          - for simplicity, we'll say it's not a circularly linked list
          - given the tail, we can "splice" out that node:
             tail = tail.prev();
             tail.setNext(null);
          - there are a few more details to deal with the case when we have < 2 nodes, but it's still a constant time operation now!
       - What if someone gives you a Node, call it d, and says that they want you delete it from the doubly linked list?
          d.prev().setNext() = d.next();
          d.next().setPrev() = d.prev();
          
          - we just splice it out
          - again, there are a few more details to deal with with respect to the head, etc., but it's now a constant time operation!
          - When could this happen?
             - delete all occurrences of a particular value
             - any time we have to iterate over the data already

  • List fast vs. slow

          ArrayList   Singly LL   Singly LL (tail)   Doubly   LL

    add to end   fast      slow      fast         fast
    add to front   slow      fast      fast         fast
    contains   slow      slow      slow         slow
    get      fast      slow      slow         slow
    insert at index   slow      slow      slow         slow
    remove      slow      slow      slow         slow (fast if given node)
    remove at end   fast      slow      slow         fast
    set      fast      slow      slow         slow
    size      fast      fast      fast         fast

  • Java also has a LinkedList class that implements the List interface
       - http://docs.oracle.com/javase/7/docs/api/java/util/LinkedList.html