Presentation is loading. Please wait.

Presentation is loading. Please wait.

Practicum 1: - Persistent vs. destructive lists - Java interfaces 15-211 Fundamental Data Structures and Algorithms Margaret Reid-Miller January 22, 2004.

Similar presentations


Presentation on theme: "Practicum 1: - Persistent vs. destructive lists - Java interfaces 15-211 Fundamental Data Structures and Algorithms Margaret Reid-Miller January 22, 2004."— Presentation transcript:

1

2 Practicum 1: - Persistent vs. destructive lists - Java interfaces 15-211 Fundamental Data Structures and Algorithms Margaret Reid-Miller January 22, 2004 Based on notes by Peter Lee

3 Reminders  HW2 is out  due on Monday at 11:59pm  Read  Chapter 17 (this should be review)

4 Integer lists in Java public class List { int head; List tail; public List(int n, List l) { head = n; tail = l; }  An integer list is either  an empty list, or  an integer paired with an integer list

5 Implementing length() and add() public class List { public static int length (List l) { if (l==null) return 0; else return 1 + length(l.tail); } public static List add (int n, List l) { if (l==null) return new List(n, null); else return new List(l.head, add(n, l.tail)); }

6 Using list objects … list1 = new List(3, null); … list2 = List.add(9, list1); … List.length(list2) … …

7 Choice 1: Destructively modifying L myList1 nil myList2

8 Choice 2: Preserving the old list nil myList1 myList2

9 Which choice does this implement? public class ListOps { public static int length (List l) { if (l==null) return 0; else return 1 + length(l.tail); } public static List add (int n, List l) { if (l==null) return new List(n, null); else return new List(l.head, add(n, l.tail)); }

10 Which choice is the right one?  Should add() be destructive (choice 1) or not (choice 2)?  Does it matter? Why or why not?

11 Implementing reverse() public static List reverse(List l) { if (l==null) return null; else { List r = reverse(l.tail); return add(l.head, r); }

12 How many list cells?  Is this version of reverse() destructive or not?  How many list objects are there after invoking reverse()?  [If you are a C/C++ hacker, be careful – We aren’t flipping pointers here!]

13 Faster reverse?  Question:  Can you write a version of reverse that runs in just n steps?  It should have the same behavior as the reverse we have shown here, i.e., it should not destroy the original list.

14 Faster reverse() public static List reverse(List l) { return rev(l, null); } private static List rev(List l, List result) { if (l==null) return result; else return rev(l.tail, new List(l.head, result)); }

15 Objects  Java is an object-oriented programming language.  This means that it encourages organizing a program’s functionality around the idea of an “object”.  But the code for Lists that we have presented here is not really object- oriented…

16 Our List code is not object-oriented public class List { int head; List tail; public List(int n, List l) { head = n; tail = l; } public class ListOps { public static int length (List l) { … } public static List add (int n, List l) { … } public static List reverse (List l) { … } } Essentially a C/C++ struct Essentially C/C++ code

17 The empty list is not an object! … list1 = new List(3,null); list2 = null … … list1.length() … … list2.length() … … Because null is not an object, method invocations can fail. Yuck!

18 Counters  As a first example of object-oriented programming, let’s define counters. No, not these kinds of counters…

19 Counters  Let’s define a counter to be an object that maintains a count that can be initialized and incremented. 0 inc resetCount readCount 1 1 0

20 Counters  Informally, a counting object  can be initialized,  can be incremented, and  can be read.  We can specify these things in Java by writing a Java interface.

21 The Countable interface /** Interface for counting objects. */ public interface Countable { /** Increment the internal counter. */ public void inc(); /** Reset the internal counter to 0. */ public void resetCount(); /** Return the internal count. */ public int readCount(); } Often an adjective

22 Interfaces  A Java interface is a kind of specification for classes of objects.  In this example, the interface specifies that any class of “countable” objects must provide the inc(), resetCount(), and readCount() methods.

23 A simple implementation /** Simple counting objects. */ public class SimpleCounter implements Countable { /** Initialization for new counters. */ public SimpleCounter () { resetCount(); } /** Reset the counter to 0. */ public void resetCount () { count = 0; } /** Increment the counter. */ public void inc () { count++; } /** Return the current count. */ public int readCount () { return count; } private int count; } The constructor method. Private instance variable.

24 Creation of new SimpleCounter objects. Using SimpleCounter /** A program that uses a SimpleCounters. */ public class Main { public static void main (String args[]) { int n = 99; Countable c = new SimpleCounter(); Countable d = new SimpleCounter(); c.inc(); d.inc(); System.out.println (n + c.readCount() + d.readCount()); }

25 Creating new objects Countable c = new SimpleCounter(); Countable d = new SimpleCounter(); c 0 inc resetCount readCount d 0 inc resetCount readCount

26 Our little program  Files we have created for our little program:  Countable.java  The interface specification.  SimpleCounter.java  An implementation of the interface.  Main.java  A client program.

27 Why use interfaces?  Strictly speaking, Java does not require us to make use of an interface for this program. public class SimpleCounter implements Countable { … }

28 Why use interfaces?  Strictly speaking, Java does not require us to make use of an interface for this program. public class SimpleCounter { … } Leaving out the implements declaration is OK with Java.

29 Why use interfaces?  Interfaces allow us to separate interface from implementation.  If properly designed, then clients assume only the interface.  Clients don’t have to change, even if the underlying implementations change.

30 29 Interface vs implementation open >>(int) close setf write read

31 30 open >>(int) close setf write read Interface vs implementation O(n 2 )

32 31 Abstract data types  When the allowable operations on a type of data are controlled in this manner, we say that we are using abstract data types.  In our first example, we have defined the ADT of counters.

33 Abstract data types  Abstract data types can make it easier to…  …make use of pre-existing code.  …work with a team.  …maintain code in the long run.  …write down the results of your careful thinking about the problem.

34 Another counting class /** Counting objects that double every time. */ public class DoublingCounter implements Countable { /** Initialization for new counters. */ public DoublingCounter () { resetCount(); } /** Reset the counter to 1. */ public void resetCount () { count = 1; } /** Increment the counter. */ public void inc () { count *= 2; } /** Return the current count. */ public int readCount () { return count; } private int count; }

35 A digression  Why the /** … */ comments?  These are special documentation comments that are used for public classes and members of classes.  When used properly, the javadoc command will create web pages for your code automatically.  (See the hw1 code.)

36 Back to Lists

37 Lists of integers public interface IntList { /** @returns the length of the list */ public int length (); /** Add n to the end of the list */ public IntList add (int n); /** @returns a String representation */ public String toString (); }

38 Inductive definitions  An integer list is either  an empty list, or  an integer paired with an integer list

39 Empty lists public class EmptyIntList implements IntList { public int length () { return 0; } public IntList add (int n) { return new IntListCell(n); } public String toString () { return “”; }

40 Non-empty lists public class IntListCell implements IntList { private int head; private IntList tail; public IntListCell (int n) { head = n; tail = new EmptyIntList(); } public IntListCell (int n, IntList tail) { head = n; tail = tail; } public int length () { return 1 + tail.length(); } public IntList add (int n) { return new IntListCell (head, tail.add(n)); }

41 How about the length method?

42 More inductive definitions  The length of a list L is  0, if L is the empty list  1 + length of the tail of L, otherwise Base case Inductive case

43 Length public class EmptyIntList implements IntList { … public int length() { return 0; } … } public class IntListCell implements IntList { … public int length() { return 1 + next.length(); } … } Base case Inductive case

44 Reverse  The reversal of a list L is:  L, if L is empty  n appended to M, otherwise  where n is the first element of L, and  M is the reversal of the tail of L

45 Reverse public class EmptyIntList implements IntList { … public IntList reverse() { return this; } … } public class IntListCell implements IntList { … public IntList reverse() { IntList t = tail.reverse(); return t.add(head); } … }

46 The empty list is an object! … myList1 = new IntListCell(3); myList2 = new EmptyIntList(); … … myList1.length() … … myList2.length() … … Since we use objects to represent empty lists, method invocations are always OK. Yay!

47 Constant factors  “My computer is 4 times faster than yours.”  So what?

48 “Big-Oh” notation N c  f(N) T(N) n0n0 running time T(N) = O(f(N)) “T(N) is order f(N)”

49 “Big-Oh” notation  Given a function T(N):  T(N) = O(f(N)) if  there is a real constant c and integer constant n 0 such that T(N)  c. f(N) for all N  n 0.  c is called the constant factor.

50 Big-Oh  When T(N) = O(f(N)), we are saying that T(N) grows no faster than f(N).  I.e., f(N) describes an upper bound on T(N).  Put another way:  For “large enough” inputs, c. f(N) always dominates T(N).  Called the asymptotic behavior

51 Big-O characteristic  If T(N) = c. f(N) then  T(N) = O(f(N))  Constant factors “don’t matter”  Because of this, when T(N) = O(c. g(N)), we usually drop the constant and just say O(g(N))

52 Big-O characteristic  Suppose T(N)= k, for some constant k  Then T(N) = O(1)  Why?  because c*1 > k, for some c

53 Big-O characteristic  More interesting:  Suppose T(N) = 20n 3 + 10nlog n + 5  Then T(N) = O(n 3 )  Lower-order terms “don’t matter”  Question:  What constants c and n 0 can be used to show that the above is true?  Answer: c=35, n 0 =1

54 From last time…  We calculated this running time for reverse()  So, in big-Oh terms:  O(n 2 )

55 Big-O characteristic  If T 1 (N) = O(f(N)) and T 2 (N) = O(g(N)) then  T 1 (N) + T 2 (N) = max(O(f(N)), O(g(N)).  The bigger task always dominates eventually.  Also:  T 1 (N)  T 2 (N) = O(f(N)  g(N)).

56 Some common functions

57 Big-Oh is imprecise  Let T(N) = 100log N  Then T(N) = O(log N)  And T(N) = O(N 2 )  And T(N) = O(N 3 )  And T(N) = O(2 N )

58 Tight bounds  Because of this imprecision, we normally try to find the “tightest” bound on the number of steps.  So, while it is true that reverse() is O(2 n ), it is more useful to use the tighter bound of O(n 2 ).

59 Big-O characteristics  log k (N) = O(N) for any constant k.  I.e, logarithms grow very slowly.

60 Note  There is a bit of a mismatch because we are counting “steps”, which are always whole numbers, but logarithms are real numbers  We will take the floor or ceiling of any real numbers  Usually this is implicitly done

61 Stacks and Queues, Revisited

62 A Stack interface public interface Stack { public void push(Object x); public void pop(); public Object top(); public boolean isEmpty(); public void makeEmpty(); }

63 Stacks are LIFO a b c d e Push operations:

64 Stacks are LIFO a b c d e Pop operation: Last element that was pushed is the first to be popped.

65 A Queue interface public interface Queue { public void enqueue(Object x); public Object dequeue(); public boolean isEmpty(); public void makeEmpty(); }

66 Queues are FIFO front back krqcm

67 Queues are FIFO front back krqcm y Enqueue operation:

68 Queues are FIFO frontback krqcmy Enqueue operation:

69 Queues are FIFO frontback krqcmy Dequeue operation:

70 Implementing stacks, 1 abc Linked representation. All operations constant time, or O(1).

71 Implementing stacks, 2  An alternative is to use an array-based representation.  What are some advantages and disadvantages of an array-based representation? abc top

72 Array representation of stacks  But what to do when the array overflows?  Can we still get O(1) operations?  We could have a linked list of arrays.  Is there another way?

73 An idea  Let’s try doubling the size of the array every time it overflows.  In more detail:  Start off with an array of size n.  When the array overflows, allocate a new array of size 2n, copy all of the previous n elements to it.  Set n=2n, and then continue by using the new array.

74 A Big 15-211 Hint  Whenever you see something doubling, immediately think of powers of 2:  2 0, 2 1, 2 2, 2 3, 2 4, … Opportunity for induction…

75 Question 1  In the worst case, what is the running time of the push operation?  I.e., how many copying operations might be needed?  Answer: O(n).  If a push() operation causes an overflow, then potentially all n elements on the stack must be copied.

76 Question 2  In the worst case, when starting with an empty stack, what is the average running time for all of the push operations in any given sequence of legal stack operations?

77 What contributes  What contributes to the running time?  The push itself  Possibility of creating a new array  Copying all of the elements to a new array Constant-time overhead This is the overhead that we have to worry about

78 Informal analysis  Let’s try counting a sequence of push operations, starting with array of size 0.  push: 0 copies (create new array of size 2 0 )  push: 1 copy (into a new array of size 2 1 )  push: 2 copies (into a new array of size 2 2 )  push push: 4 copies (into a new array of size 2 3 )  push push push push: 8 copies (into a new array of size 2 4 )

79 Informal analysis, cont’d copy operations are needed. So, on average, one copy is needed for every push operation. 2 n-1 +2 n-2 +2 n-3 …+2 0 =  2 n For 2 n push operations, a grand total of

80 Question 2, again  In the worst case, when starting with an empty stack, what is the average running time for all of the push operations in any given sequence of legal stack operations?  Answer: O(1)  Each push might require the creation of a new array plus, on average, a copy.

81 Amortized running time  We say that our array-based implementation of stacks runs in amortized constant time.  The J2SE java.util.ArrayList class is based on this representation.java.util.ArrayList

82 A question to ponder…  Question: How would you use an implementation of the Stack interface to implement the Queue interface?  What would be the running time of the operations?

83 A queue from two stacks f g h i j Enqueue: e d c b a Dequeue: What happens when the stack on the right becomes empty?


Download ppt "Practicum 1: - Persistent vs. destructive lists - Java interfaces 15-211 Fundamental Data Structures and Algorithms Margaret Reid-Miller January 22, 2004."

Similar presentations


Ads by Google