Survey
* Your assessment is very important for improving the workof artificial intelligence, which forms the content of this project
* Your assessment is very important for improving the workof artificial intelligence, which forms the content of this project
Sorting and Searching "Why were you searching for me? Did you not know that I must be in my Father's house?" Two links showing animated sorts: http://www.cs.oswego.edu/~mohammad/classes/csc241/samples/sort/Sort2-E.html http://maven.smith.edu/~thiebaut/java/sort/demo.html Chapter 12 Litvin has code for the AP tested sorts: Insertion, Selection, and Merge. These are included on the Student Disk. HW from Litvin: problems 4, 7, 8 on p 343-344. Also do the Assignments on this handout. Some of the following material is from Java: How to Program, 6th Edition (Deitel). Searching is an important topic in computer science (see Google). Searching (looking for an item (the search key) in a dataset, and returning its location) is most efficient if the data to be searched is sorted. The search for efficient (fast, consuming little memory) sorts has resulted in methods embodied in classes such as Java's Arrays class (see http://www.leepoint.net/notes-java/data/arrays/70sorting.html). Linear Search Data need not be sorted for a linear search. For searching small data sets, this search method works well. It runs in O(n) (pronounced "on the order of n") time. Big O notation shows how a method's run time grows as the number of items processed increases. If your data set gets 1000 times bigger, linear search takes 1000 times longer (in the worst case, having to compare the search key to every item of data). // Fig 16.2: LinearArray.java from Deitel // Class that contains an array of random integers and a method // that will search that array sequentially. import java.util.Random; public class LinearArray { private int[] data; // array of values private static Random generator = new Random(); // create array of given size and fill with random integers public LinearArray( int size ) { data = new int[ size ]; // create space for array // fill array with random ints in range 10-99 for ( int i = 0; i < size; i++ ) data[ i ] = 10 + generator.nextInt( 90 ); } // end LinearArray constructor // perform a linear search on the data public int linearSearch( int searchKey ) { // loop through array sequentially for ( int index = 0; index < data.length; index++ ) 1 if ( data[ index ] == searchKey ) return index; // return index of integer return -1; // integer was not found } // end method linearSearch // method to output values in array public String toString() { StringBuffer temporary = new StringBuffer(); // iterate through array for ( int element : data ) temporary.append( element + " " ); temporary.append( "\n" ); // add endline character return temporary.toString(); } // end method toString } // end class LinearArray // Fig 16.3.: LinearSearchTest.java from Deitel // Sequentially search an array for an item. import java.util.Scanner; public class LinearSearchTest { public static void main( String args[] ) { // create Scanner object to input data Scanner input = new Scanner( System.in ); int searchInt; // search key int position; // location of search key in array // create array and output it LinearArray searchArray = new LinearArray( 10 ); System.out.println( searchArray ); // print array // get input from user System.out.print( "Please enter an integer value (-1 to quit): " ); searchInt = input.nextInt(); // read first int from user // repeatedly input an integer; -1 terminates the program while ( searchInt != -1 ) { // perform linear search position = searchArray.linearSearch( searchInt ); if ( position == -1 ) // integer was not found System.out.println( "The integer " + searchInt + " was not found.\n" ); else // integer was found System.out.println( "The integer " + searchInt + " was found in position " + position + ".\n" ); // get input from user System.out.print( "Please enter an integer value (-1 to quit): " ); searchInt = input.nextInt(); // read next int from user } // end while } // end main 2 } // end class LinearSearchTest Note the use of the Random class. All objects of the LinearArray class refer to the same (static) copy of the random number generator. The nextInt( 90 ) method returns an int from 0 to 89 (inclusive); adding 10 shifts the values in the array to [10, 99]. Note the use of the enhanced for loop (a Java 5 feature): for (declaration : expression) { statement } Here declaration is a variable of the same type as the elements stored in expression. Thus // iterate through array for ( int element : data ) temporary.append( element + " " ); is equivalent to: // iterate through array for ( int i = 0; i < data.length; i++ ) temporary.append( data[i] + " " ); Assignment 1: Modify the above programs to sort an array of words (type String). Hint: Here is a code snippet to generate random six character long words. private static Random rand = new Random(); private static char random() { return (char) ('A' + rand.nextInt(52)); } // generate "random" word of 6 characters public static void fill(String[] words) { for (int i = 0; i < words.length; i++) { StringBuffer b = new StringBuffer(); for (int j = 0; j < 6; j++) b.append((char) random()); words[i] = b.toString(); } } 3 Binary Search This is a much faster sorting algorithm, but your data must first be sorted. The search compares the search key to the item in the middle of the data list. If it does not match, it divides the list in half, continuing to search only in the appropriate half of the (sorted) list. For example, if the search key is smaller than the middle data item, it would continue searching only the half of the list "left" of the middle element. How many comparisons must be made in the worst case? Divide the number of data elements by 2 repeatedly, rounding down since we remove the middle item. When you reach 0, count the number of times you divided by 2 - that is the number of "passes" the search may have to make through the data. Thus we need the exponent of the first power of 2 bigger than the number n of data elements, or log2n. Since all logs grow at about the same rate, we say the run time or this search method is O(log n) or logarithmic run time. But the data must first be sorted for this to work! // Fig 16.4: BinaryArray.java from Deitel // Class that contains an array of random integers and a method // that uses binary search to find an integer. import java.util.Random; import java.util.Arrays; public class BinaryArray { private int[] data; // array of values private static Random generator = new Random(); // create array of given size and fill with random integers public BinaryArray( int size ) { data = new int[ size ]; // create space for array // fill array with random ints in range 10-99 for ( int i = 0; i < size; i++ ) data[ i ] = 10 + generator.nextInt( 90 ); Arrays.sort( data ); } // end BinaryArray constructor // perform a binary search on the data public int binarySearch( int searchElement ) { int low = 0; // low end of the search area int high = data.length - 1; // high end of the search area int middle = ( low + high + 1 ) / 2; // middle element int location = -1; // return value; -1 if not found do // loop to search for element { // print remaining elements of array System.out.print( remainingElements( low, high ) ); // output spaces for alignment for ( int i = 0; i < middle; i++ ) System.out.print( " " ); System.out.println( " * " ); // indicate current middle 4 // if the element is found at the middle if ( searchElement == data[ middle ] ) location = middle; // location is the current middle // middle element is too high else if ( searchElement < data[ middle ] ) high = middle - 1; // eliminate the higher half else // middle element is too low low = middle + 1; // eliminate the lower half middle = ( low + high + 1 ) / 2; // recalculate the middle } while ( ( low <= high ) && ( location == -1 ) ); return location; // return location of search key } // end method binarySearch // method to output certain values in array public String remainingElements( int low, int high ) { StringBuffer temporary = new StringBuffer(); // output spaces for alignment for ( int i = 0; i < low; i++ ) temporary.append( " " ); // output elements left in array for ( int i = low; i <= high; i++ ) temporary.append( data[ i ] + " " ); temporary.append( "\n" ); return temporary.toString(); } // end method remainingElements // method to output values in array public String toString() { return remainingElements( 0, data.length - 1 ); } // end method toString } // end class BinaryArray // Fig 16.5: BinarySearchTest.java from Deitel // Use binary search to locate an item in an array. import java.util.Scanner; public class BinarySearchTest { public static void main( String args[] ) { // create Scanner object to input data Scanner input = new Scanner( System.in ); int searchInt; // search key int position; // location of search key in array // create array and output it BinaryArray searchArray = new BinaryArray( 15 ); System.out.println( searchArray ); // get input from user System.out.print( "Please enter an integer value (-1 to quit): " ); 5 searchInt = input.nextInt(); // read an int from user System.out.println(); // repeatedly input an integer; -1 terminates the program while ( searchInt != -1 ) { // use binary search to try to find integer position = searchArray.binarySearch( searchInt ); // return value of -1 indicates integer was not found if ( position == -1 ) System.out.println( "The integer " + searchInt + " was not found.\n" ); else System.out.println( "The integer " + searchInt + " was found in position " + position + ".\n" ); // get input from user System.out.print( "Please enter an integer value (-1 to quit): " ); searchInt = input.nextInt(); // read an int from user System.out.println(); } // end while } // end main } // end class BinarySearchTest Note the line "Arrays.sort( data );". This uses the Arrays class (note "import java.util.Arrays;" built-in sort method to assure the random list of integers data is sorted before the search begins. Most programmers will use the built-in sort (there is also a binarySearch( ) method!) and not write their own sort or search method. The sort is a variation of the Quick sort discussed below, working in n*log(n) time, which is must faster for large n than the O(n2) time required for simple sorts. Assignment 2: Run this for 15 sorted items. How much longer do you think it would take to search 1,048,575 already sorted items? Try it and report your results. What if the items were not sorted to at the outset? How about linear search? 6 Bubble Sort void BubbleSort(int a[]) { int i,j; for (i=MAXLENGTH; --i >=0;) { swapped = 0; for (j=0; j<i;j++) { if (a[j]>a[j+1]) { Swap[a[j],a[j+1]); swapped=1; } } if (!swapped) return; } } This is the quickest version of a bubble sort, where the big, heavy numbers sink to the bottom of the list and the light, small numbers bubble to the top. Note it does not compare the final numbers in the data set after they have settled into their resting place (the inner loop only goes to i, which gets one smaller on each pass). It also uses a flag variable (swapped) to indicate if a swap was made on a given pass. If an entire pass goes by without a swap, the method ends, perhaps before completing the worst case n - 1 passes through the n data elements. Still, this is a very slow O(n2) sort. It is quick and easy, but in part because better methods exist, it is not covered on the AP exam. Quick Sort This is quick, and variations of this are among the fastest of all sorts: O(n*log(n)). Following is a listing of this recursive sort. This is not covered on the AP exam. (A merge sort is similar and is on the AP exam). First introduced 1960. int FindPivot(int A[],int l, int r) { switch (choice) { case 1: return l; case 2: return pivot2(A,l,h) case 3: return l+random(r-l); } } int partition(int A[], int l, int r) { int i,pivot, pivotpos; pivotpos = FindPivot(A,l,r); swap(&A[l],&A[pivotpos]); pivotpos = l; pivot = A[pivotpos]; for (i = l+1; i <= r; i++) { 7 if (A[i] < pivot) { pivotpos++; swap(&A[pivotpos],&A[i]); } } swap(&A[l],&A[pivotpos]); return pivotpos; } void QuickSort(int A[], int l,int r, int threshold) { int i, pivot; if (r-l>threshold) { delay(CompareDelay); pivot = partition(A,l,r); QuickSort(A,l,pivot-1,threshold); QuickSort(A,pivot+1,r,threshold); } } int pivot2(int A[], int l, int r) { int i = (r+l)/2; if ((A[l] return if ((A[r] return if ((A[r] return if ((A[i] return if ((A[l] return if ((A[i] return <= i; <= i; <= l; <= l; <= r; <= r; A[i]) && (A[i] <= A[r])) A[i]) && (A[i] <= A[l])) A[l]) && (A[l] <= A[i])) A[l]) && (A[l] <= A[r])) A[r]) && (A[r] <= A[i])) A[r]) && (A[r] <= A[l])) } To learn more about this sort, see: http://maven.smith.edu/~thiebaut/java/sort/demo.html or http://www.cs.oswego.edu/~mohammad/classes/csc241/samples/sort/Sort2-E.html. 8 Selection Sort This is easy to understand, very predictable, but slow. Find the smallest item in the list and put it first. Find the smallest item of the remaining unsorted items and swap with the second item in the list. Continue until you find the smallest item of a small list and swap with the next to last item. The largest item is left at the end. For example, starting with: 34 56 04 10 77 51 93 30 05 52 Find smallest item. 04, swap with item at start of list (index or subscript 0). Thus: 04 56 34 10 77 51 93 30 05 52 after pass 1 (underline denotes finished part of list; bold, swapped items). Start with the 56 in list (subscript or index 1) and find smallest item in list to right; swap with the 56. Thus: 04 05 34 10 77 51 93 30 56 52 after pass 2 Assignment 3: Consider the following array of integers. 25 16 49 36 81 64 33 11 22 55 By hand, follow the code and indicate the step by step output of the sort. Compare with the output obtained by modifying the code so the data array is not random, but contains the numbers above. // Fig 16.6: SelectionSort.java from Deitel import java.util.Random; public class SelectionSort { private int[] data; // array of values private static Random generator = new Random(); // create array of given size and fill with random integers public SelectionSort( int size ) { data = new int[ size ]; // create space for array // fill array with random ints in range 10-99 for ( int i = 0; i < size; i++ ) data[ i ] = 10 + generator.nextInt( 90 ); } // end SelectionSort constructor // sort array using selection sort public void sort() { int smallest; // index of smallest element // loop over data.length - 1 elements for ( int i = 0; i < data.length - 1; i++ ) { smallest = i; // first index of remaining array // loop to find index of smallest element for ( int index = i + 1; index < data.length; index++ ) if ( data[ index ] < data[ smallest ] ) smallest = index; swap( i, smallest ); // swap smallest element into position printPass( i + 1, smallest ); // output pass of algorithm } // end outer for } // end method sort 9 // helper method to swap values in two elements public void swap( int first, int second ) { int temporary = data[ first ]; // store first in temporary data[ first ] = data[ second ]; // replace first with second data[ second ] = temporary; // put temporary in second } // end method swap // print a pass of the algorithm public void printPass( int pass, int index ) { System.out.print( String.format( "after pass %2d: ", pass ) ); // output elements till selected item for ( int i = 0; i < index; i++ ) System.out.print( data[ i ] + " " ); System.out.print( data[ index ] + "* " ); // indicate swap // finish outputting array for ( int i = index + 1; i < data.length; i++ ) System.out.print( data[ i ] + " " ); System.out.print( "\n " ); // for alignment // indicate amount of array that is sorted for( int j = 0; j < pass; j++ ) System.out.print( "-- " ); System.out.println( "\n" ); // add endline } // end method printPass // method to output values in array public String toString() { StringBuffer temporary = new StringBuffer(); // iterate through array for ( int element : data ) temporary.append( element + " " ); temporary.append( "\n" ); // add endline character return temporary.toString(); } // end method toString } // end class SelectionSort // Fig 16.7: SelectionSortTest.java public class SelectionSortTest { public static void main( String[] args ) { // create object to perform selection sort SelectionSort sortArray = new SelectionSort( 10 ); System.out.println( "Unsorted array:" ); System.out.println( sortArray ); // print unsorted array sortArray.sort(); // sort array System.out.println( "Sorted array:" ); System.out.println( sortArray ); // print sorted array } // end main } // end class SelectionSortTest 10 Insertion Sort This works quickly for arrays that are almost in order. Start with the second data item and compare it to the first. If out of order, swap. On the next pass, look at the third data item; insert it into the correct position with respect to the first two items. After i passes, the first i data items will be sorted. If the original array is: 34 56 04 10 77 51 93 30 05 52 Comparing the first two items, leave them alone as already in order. After pass 1, we have: 34 56* 04 10 77 51 93 30 05 52 The asterisk marks the item inserted, bold items are in order. Consider the third item, 04. It is less than 56. Temporarily store the 04 and shift 56 one spot right. Compare 04 and 34 - we need to swap, so shift the 34 right one space. Place the 04 at the start of the list. We have: 04* 34 56 10 77 51 93 30 05 52 Now put the 10 into temporary storage. Comparing to 56, we move 56 one step right. Comparing 10 to 34, we move 34 one step right. Comparing to 04, we put the 10 into "second position" in the list. We have: 04 10* 34 56 77 51 93 30 05 52 The following code from Deitel puts dashes under the sorted portion of the list, and places an asterisk by the element inserted into place on a given pass. // Fig 16.8: InsertionSort.java from Deitel // Class that creates an array filled with random integers. // Provides a method to sort the array with insertion sort. import java.util.Random; public class InsertionSort { private int[] data; // array of values private static Random generator = new Random(); // create array of given size and fill with random integers public InsertionSort( int size ) { data = new int[ size ]; // create space for array // fill array with random ints in range 10-99 for ( int i = 0; i < size; i++ ) data[ i ] = 10 + generator.nextInt( 90 ); } // end InsertionSort constructor // sort array using insertion sort public void sort() { int insert; // temporary variable to hold element to insert // loop over data.length - 1 elements for ( int next = 1; next < data.length; next++ ) { // store value in current element insert = data[ next ]; // initialize location to place element 11 int moveItem = next; // search for place to put current element while ( moveItem > 0 && data[ moveItem - 1 ] > insert ) { // shift element right one slot data[ moveItem ] = data[ moveItem - 1 ]; moveItem--; } // end while data[ moveItem ] = insert; // place inserted element printPass( next, moveItem ); // output pass of algorithm } // end for } // end method sort // print a pass of the algorithm public void printPass( int pass, int index ) { System.out.print( String.format( "after pass %2d: ", pass ) ); // output elements till swapped item for ( int i = 0; i < index; i++ ) System.out.print( data[ i ] + " " ); System.out.print( data[ index ] + "* " ); // indicate swap // finish outputting array for ( int i = index + 1; i < data.length; i++ ) System.out.print( data[ i ] + " " ); System.out.print( "\n " ); // for alignment // indicate amount of array that is sorted for( int i = 0; i <= pass; i++ ) System.out.print( "-- " ); System.out.println( "\n" ); // add endline } // end method printPass // method to output values in array public String toString() { StringBuffer temporary = new StringBuffer(); // iterate through array for ( int element : data ) temporary.append( element + " " ); temporary.append( "\n" ); // add endline character return temporary.toString(); } // end method toString } // end class InsertionSort // Fig 16.9: InsertionSortTest.java // Test the insertion sort class. public class InsertionSortTest { public static void main( String[] args ) { // create object to perform insertion sort InsertionSort sortArray = new InsertionSort( 10 ); System.out.println( "Unsorted array:" ); 12 System.out.println( sortArray ); // print unsorted array sortArray.sort(); // sort array System.out.println( "Sorted array:" ); System.out.println( sortArray ); // print sorted array } // end main } // end class InsertionSortTest Assignment 4: Consider the following array of integers. 25 16 49 36 81 64 33 11 22 55 By hand, follow the code and indicate the step by step output of the sort. Compare with the output obtained by modifying the code so the data array is not random, but contains the numbers above. 13 Merge Sort Split the data array in half; sort each subarray, then merge into one larger array. The following method is recursive; it stops (the base case) when arrays have a size of 1 (and must be sorted) and calls itself to sort the subarrays. Consider the data array: 75 56 85 90 49 26 12 48 40 47 Split in half: 75 56 85 90 49 26 12 48 40 47 75 56 85 90 49 26 12 48 40 47 Again: 75 56 85 90 49 75 56 85 90 49 Again: 75 56 85 75 56 85 Again: 75 56 75 56 Merge: 75 note the "splits" are unequal 56 56 75 Continue Assignment 5: Continue the above sort by hand and compare to the output when the program is run. Follow the code and be sure you understand how the split and merge functions are accomplished. // Figure 16.10: MergeSort.java from Deitel // Class that creates an array filled with random integers. // Provides a method to sort the array with merge sort. import java.util.Random; public class MergeSort { private int[] data; // array of values private static Random generator = new Random(); // create array of given size and fill with random integers public MergeSort( int size ) { data = new int[ size ]; // create space for array // fill array with random ints in range 10-99 14 for ( int i = 0; i < size; i++ ) data[ i ] = 10 + generator.nextInt( 90 ); } // end MergeSort constructor // calls recursive split method to begin merge sorting public void sort() { sortArray( 0, data.length - 1 ); // split entire array } // end method sort // splits array, sorts subarrays and merges subarrays into sorted array private void sortArray( int low, int high ) { // test base case; size of array equals 1 if ( ( high - low ) >= 1 ) // if not base case { int middle1 = ( low + high ) / 2; // calculate middle of array int middle2 = middle1 + 1; // calculate next element over // output split step System.out.println( "split: System.out.println( " System.out.println( " System.out.println(); " + subarray( low, high ) ); " + subarray( low, middle1 ) ); " + subarray( middle2, high ) ); // split array in half; sort each half (recursive calls) sortArray( low, middle1 ); // first half of array sortArray( middle2, high ); // second half of array // merge two sorted arrays after split calls return merge ( low, middle1, middle2, high ); } // end if } // end method split // merge two sorted subarrays into one sorted subarray private void merge( int left, int middle1, int middle2, int right ) { int leftIndex = left; // index into left subarray int rightIndex = middle2; // index into right subarray int combinedIndex = left; // index into temporary working array int[] combined = new int[ data.length ]; // working array // output two subarrays before merging System.out.println( "merge: " + subarray( left, middle1 ) ); System.out.println( " " + subarray( middle2, right ) ); // merge arrays until reaching end of either while ( leftIndex <= middle1 && rightIndex <= right ) { // place smaller of two current elements into result // and move to next space in arrays if ( data[ leftIndex ] <= data[ rightIndex ] ) combined[ combinedIndex++ ] = data[ leftIndex++ ]; else combined[ combinedIndex++ ] = data[ rightIndex++ ]; } // end while // if left array is empty if ( leftIndex == middle2 ) // copy in rest of right array while ( rightIndex <= right ) combined[ combinedIndex++ ] = data[ rightIndex++ ]; else // right array is empty 15 // copy in rest of left array while ( leftIndex <= middle1 ) combined[ combinedIndex++ ] = data[ leftIndex++ ]; // copy values back into original array for ( int i = left; i <= right; i++ ) data[ i ] = combined[ i ]; // output merged array System.out.println( " System.out.println(); } // end method merge " + subarray( left, right ) ); // method to output certain values in array public String subarray( int low, int high ) { StringBuffer temporary = new StringBuffer(); // output spaces for alignment for ( int i = 0; i < low; i++ ) temporary.append( " " ); // output elements left in array for ( int i = low; i <= high; i++ ) temporary.append( " " + data[ i ] ); return temporary.toString(); } // end method subarray // method to output values in array public String toString() { return subarray( 0, data.length - 1 ); } // end method toString } // end class MergeSort // Figure 16.11: MergeSortTest.java // Test the merge sort class. public class MergeSortTest { public static void main( String[] args ) { // create object to perform merge sort MergeSort sortArray = new MergeSort( 10 ); // print unsorted array System.out.println( "Unsorted:" + sortArray + "\n" ); sortArray.sort(); // sort array // print sorted array System.out.println( "Sorted: } // end main } // end class MergeSortTest " + sortArray ); 16