CS62 - Spring 2011 - Lecture 11

  • video: sorting
       - http://www.youtube.com/watch?v=t8g-iYGHpEA

  • Admin
       - Assignment 3
       - thanks for the videos... keep them coming!

  • programming tips of the day
       - General debugging tips
          - incremental development
          - if you have a bug or an exception, narrow down where the problem could be
             - we'll teach you how to use the debugger (and other techniques soon)
             - use print statements to figure out the values of things
             - use print statements to isolate where the problem is
             - if you've done incremental development, you'll also drastically narrow down the possible locations
          - exceptions
             - what are they?
             - why do they happen?

       - ternary operator
          - say we have two variables "first" and "second" and we want to assign the value of the larger of the two to a third variable "largest". How would we do it?
             - one way:
                if( first > second ){
                   largest = first;
                }else{
                   largest = second;
                }
             - another way:
                largest = first > second ? first : second;
             - the ternary operator takes three parts:
                <condition> ? <expression1> : <expression2>
                - if the condition is true, the value of the ternary expression is <expression1>
                - if the condition is fals, the value of the ternary expression is <expression2>
                - you can simplify many statements using this

  • Lists (http://java.sun.com/j2se/1.4.2/docs/api/java/util/List.html)

  • Linked lists
       - look at the Node class in LinkedList code again to refresh our memories
       - A linked list is a List data structure made up of Nodes
       - draw a pictures again of an ArrayList and LinkedList
       - if someone gave us the first node, how would we traverse the linked list?
          Node<E> 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 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 LinkedList code)
          - public void addFirst(E value)
             - we need to associate head with a new value
             - and set the "next" element for the new head to be the old head
             - this is accomplished at the same time, using the Node constructor
          - public E removeFirst()
             - save the value of head so we can return it
             - set head to be head.next()
             - return the value of the original head
          - public boolean contains(E value)
             - start at the head and traverse through the nodes
             - we can just keep a Node<E> reference and update it with .next() to move to the next node
             - stop when we either find it or we run off the end
          - Could we do this recursively?
             - we'd need a helper function: take in the value AND the current node
             - base case: if the current node is null, return false
             - recursive case:
                - if it's the current node, return it
                - otherwise, recurse on one node down the list
          - public void clear()
             - just need to set head = null and we're done
          - A few more interesting ones to try:
             - public void remove(E value)
             - public int indexOf(E value)
             - public E get(int index)
             - public addLast(E value)

  • What does the linked list buy us?
       - add and remove at the front of the list in constant time!
       - what are the run-times of the other operations?
          - 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
       - addLast 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 addLast(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 methods slightly more complicated

  • 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

  • Doubly linked list
       - 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?
             - if we had an iterator over the list and we implemented the "remove()" method