1 CSC 427: Data Structures and Algorithm AnalysisFall 2007 Inheritance and efficiency ArrayList SortedArrayList tradeoffs with adding/searching timing code divide-and-conquer algorithms
2 Dictionary revisited recall the Dictionary class from last weekimport java.util.List; import java.util.ArrayList; import java.util.Scanner; import java.io.File; public class Dictionary { private List
3 Timing dictionary searcheswe can use the java.util.Date class to verify the O(N) efficiency dict. size insert time 38, msec 77, msec 144, msec dict. size search time 38, msec 77, msec 144, msec execution time roughly doubles as dictionary size doubles import java.util.Scanner; import java.io.File; import java.util.Date; public class DictionaryTimer { public static void main(String[] args) { System.out.println("Enter name of dictionary file:"); Scanner input = new Scanner(System.in); String dictFile = input.next(); Date start1 = new Date(); Dictionary dict = new Dictionary(dictFile); Date end1 = new Date(); System.out.println(end1.getTime()-start1.getTime()); Date start2 = new Date(); for (int i = 0; i < 100; i++) { dict.contains("zzyzyba"); } Date end2 = new Date(); System.out.println((end2.getTime()-start2.getTime())/100.0);
4 Sorting the list if searches were common, then we might want to make use of binary search this requires sorting the words first, however we could change the Dictionary class to do the sorting and searching a more general solution would be to extend the ArrayList class to SortedArrayList could then be used in any application that called for a sorted list recall: public class java.util.ArrayList
5 SortedArrayList (v.1) using inheritance, we only need to redefine what is new add method sorts after adding; indexOf uses binary search no additional fields required big-Oh for add? big-Oh for indexOf? import java.util.ArrayList; import java.util.Collections; public class SortedArrayList
6 SortedArrayList (v.2) is this version any better? when?big-Oh for add? big-Oh for indexOf? import java.util.ArrayList; import java.util.Collections; public class SortedArrayList
7 SortedArrayList (v.3) if insertions and searches are mixed, sorting for each insertion/search is extremely inefficient instead, could take the time to insert each item into its correct position big-Oh for add? big-Oh for indexOf? import java.util.ArrayList; import java.util.Collections; public class SortedArrayList
8 Dictionary using SortedArrayListnote that repeated calls to add serve as insertion sort dict. size insert time 38, sec 77, sec 144, sec dict. size search time 38, msec 77, msec 144, msec insertion time roughly quadruples as dictionary size doubles; search time is trivial import java.util.Scanner; import java.io.File; import java.util.Date; public class DictionaryTimer { public static void main(String[] args) { System.out.println("Enter name of dictionary file:"); Scanner input = new Scanner(System.in); String dictFile = input.next(); Date start1 = new Date(); Dictionary dict = new Dictionary(dictFile); Date end1 = new Date(); System.out.println(end1.getTime()-start1.getTime()); Date start2 = new Date(); for (int i = 0; i < 100; i++) { dict.contains("zzyzyba"); } Date end2 = new Date(); System.out.println((end2.getTime()-start2.getTime())/100.0);
9 SortedArrayList (v.4) if adds tend to be done in groups (as in loading the dictionary) it might pay to perform lazy insertions & keep track of whether sorted big-Oh for add? big-Oh for indexOf? if desired, could still provide addInOrder method (as before) import java.util.ArrayList; import java.util.Collections; public class SortedArrayList
10 Timing dictihe lazy dictionaryonary searchesmodify the Dictionary class to use the lazy SortedArrayList dict. size insert time 38, msec 77, msec 144, msec dict. size 1st search 38, msec 77, msec 144, msec dict. size search time 77, msec 144, msec import java.util.Scanner; import java.io.File; import java.util.Date; public class DictionaryTimer { public static void main(String[] args) { System.out.println("Enter name of dictionary file:"); Scanner input = new Scanner(System.in); String dictFile = input.next(); Date start1 = new Date(); Dictionary dict = new Dictionary(dictFile); Date end1 = new Date(); System.out.println(end1.getTime()-start1.getTime()); Date start2 = new Date(); dict.contains("zzyzyba"); Date end2 = new Date(); System.out.println(end2.getTime()-start2.getTime()); Date start3 = new Date(); for (int i = 0; i < 100; i++) { } Date end3 = new Date(); System.out.println((end3.getTime()-start3.getTime())/100.0);
11 Divide & Conquer algorithmsrecursive algorithms such as binary search and merge sort are known as divide & conquer algorithms the divide & conquer approach tackles a complex problem by breaking into smaller pieces, solving each piece, and combining into an overall solution e.g., to binary search a list, check the midpoint then binary search the appropriate half of the list divide & conquer is applicable when a problem can naturally be divided into independent pieces e.g., merge sort divided the list into halves, conquered (sorted) each half, then merged the results
12 Iterative vs. divide & conquermany iterative algorithms can naturally be characterized as divide-and-conquer sequential search for X in list[0..N-1] = false if N == 0 true if X == list[0] sequential search for X in list[1..N-1] otherwise sum of list[0..N-1] = 0 if N == 0 list[0] + sum of list[1..N-1] otherwise number of occurrences of X in a list[0..N-1] = number of occurrences of X in list[1..N-1] if X != list[0] 1 + number of occurrences of X in list[1..N-1] if X == list[0] interesting, but not very useful from a practical side (iteration is faster)
13 Euclid's algorithm one of the oldest known algorithms is Euclid's algorithm for calculating the greatest common divisor (gcd) of two integers appeared in Euclid's Elements around 300 B.C., but may be even 200 years older defines the gcd of two numbers recursively, in terms of the gcd of smaller numbers /** Calculates greatest common divisor of a and b * @param a a positive integer * @param b a positive integer (a >= b) * @return the GCD of a and b */ public int gcd(int a, int b) { if (b == 0) { return a; } else { return gcd(b, a % b); e.g.., gcd(32, 12) = gcd(12, 8) = gcd(8, 4) = gcd(4, 0) = 4 e.g.., gcd(1024, 96) = gcd(96, 64) = gcd(64, 32) = gcd(32, 0) = 32 e.g.., gcd(17, 5) = gcd(5, 2) = gcd(2, 1) = gcd(1, 0) = 1 if the larger number has N digits, Euclid's algorithm requires at most O(N) recursive calls however, each (a % b) requires O(N) steps O(N2) there is no known algorithm with better big-Oh (but is possible to reduce constants)
14 Multidimensional divide & conquerwe will see later that divide & conquer is especially useful when manipulating multidimensional structures e.g., print values in a binary tree public void traverse(TreeNode