Sorting & Searching (and a Tower) Sorting Sorting is the process of arranging a list of items into a particular order There must be some value on which the order is based There are many algorithms for sorting a list of items These algorithms vary in efficiency We will examine three specific algorithms: Selection Sort, Insertion Sort, and Quick Sort Selection Sort The approach of Selection Sort: select one value and put it in its final place in the sort list repeat for all other values An expanded version: scan the list to find the smallest value put it in the first position find the next smallest value put it in the second position repeat until all values are placed
Selection Sort (cont.) An example: original: 3 9 6 1 2 smallest is 1: 1 9 6 3 2 smallest is 2: 1 2 6 3 9 smallest is 3: 1 2 3 6 9 smallest is 6: 1 2 3 6 9 public static void doselectionsort (int[] numbers) int min; int temp; for (int index = 0; index < numbers.length - 1; index++) min = index; for (int scan = index + 1; scan < numbers.length; scan++) if (numbers[scan] < numbers[min]) min = scan; // swap the values temp = numbers[min]; numbers[min] = numbers[index]; numbers[index] = temp; } // end for } // method doselectionsort
Insertion Sort The approach of Insertion Sort: Pick any item and insert it into its proper place in a sorted sublist repeat until all items have been inserted An expanded version: consider the first item to be a sorted sublist (of one item) insert the second item into the sorted sublist, shifting items as necessary to make room to insert the new addition insert the third item into the sorted sublist (of two items), shifting as necessary repeat until all values are inserted into their proper position An example: original: 3 9 6 1 2 insert 9: 3 9 6 1 2 insert 6: 3 6 9 1 2 insert 1: 1 3 6 9 2 insert 2: 1 2 3 6 9 public static void doinsertionsort (int[] numbers) for (int index = 1; index < numbers.length; index++) int key = numbers[index]; int position = index; // shift larger values to the right while (position > 0 && numbers[position-1] > key) numbers[position] = numbers[position-1]; position--; } // end while numbers[position] = key; } // end for } // method doinsertionsort
Quick Sort More efficient sort using recursion. Can also be iterative (can you figure out how?) Strategy: Divide into 2 halves according to arbitrary index value left lower than index value right greater than index value Apply strategy to each division recursively until all items sorted public void doquicksort (int[] numbers, int start, int end) if (start < end) int pivot = partition(numbers, start, end); doquicksort (start, pivot - 1); doquicksort (pivot + 1, end); } // end if } // method doquicksort private int partition (int[] numbers, int start, int end) int pivotvalue = numbers[start]; int pivotindex = start; int temp; for (int index = start + 1; index <= end; index++) if (numbers[index] < pivotvalue) pivotindex++; temp = numbers[pivotindex]; numbers[pivotindex] = numbers[index]; numbers[index] = temp; } // end if } // end for // switch pivot value into proper position temp = numbers[start]; numbers[start] = numbers[pivotindex]; numbers[pivotindex] = temp; return pivotindex; } // method partition
Comparing Sorts Both Selection and Insertion Sorts are similar in efficiency The both have outer loops that scan all elements, and inner loops that compare the value of the outer loop with almost all values in the list That is approximately n 2 number of comparisons for a list of size n We therefore say that these sorts are of order n 2 Quick Sort is (can be) more efficient: order n log 2 n Orders of Magnitude (lowest to highest most efficent to least efficient) O(1) - Constant O(log 2 n) - Logarithmic O(n) - Linear O(n log 2 n) - N log N O(n 2 ) - Quadratic O(n 3 ) - Cubic O(a n ) - Exponential O(n!) - Factorial Input Size 1 2 4 8 16 32 64 O(1) 1 1 1 1 1 1 1 O(log n) 2 0 1 2 3 4 5 6 O(n) 1 2 4 8 16 32 64 O(n log n) 2 0 2 8 24 64 160 384 O(n 2 ) 1 4 16 64 256 1024 4096 O(n 3 ) 1 8 64 512 4096 32768 2.6x10 5 O(2 n ) 2 4 16 256 65536 4294967296 1.85x10 19 O(n!) 1 2 24 40320 2.09x10 13 2.63x10 35 1.27x10 89
Towers of Hanoi The Towers of Hanoi is a puzzle made up of three vertical pegs and several disks that slide on the pegs The disks are of varying size, initially placed on one peg with the largest disk on the bottom with increasingly smaller ones on top The goal is to move all of the disks from one peg to another under the following rules: o We can move only one disk at a time o We cannot move a larger disk on top of a smaller one o Move the stack of disks from the starting peg to one of the other pegs, moving only one disk at a time, and without placing a larger disk on top of a smaller disk. The Towers of Hanoi problem is of exponential complexity!! (2 n - 1)
Towers of Hanoi Manual Solution Towers of Hanoi Programmed Recursively int totaldisks = 5; movetower (totaldisks, 1, 3, 2); public void movetower (int numdisks, int start, int end, int temp) if (numdisks == 1) moveonedisk (start, end); movetower (numdisks - 1, start, temp, end); moveonedisk (start, end); movetower (numdisks - 1, temp, end, start); } } private void moveonedisk (int start, int end) System.out.println ("Move one disk from " + start + " to " + end); } Towers of Hanoi Animated
Searching The goal is to find a particular target value In an unsorted list, a linear search is a simple technique Scan through the list one item at a time in order looking for the target value If the list is sorted, not all items need to be examined As soon as you encounter a value greater than the target value, you know the target is not in the list Iterative Binary Search If the list is sorted, we can use a binary search to find the target value Strategy: Examine the item in the middle of the list, eliminating half of the items from consideration The target value will either be found, or will be in one half or the other Continue the process, examining the middle item of the search section on each comparison public static int iterativebinarysearch (int[] numbers, int target) int index; int left = 0 int right = numbers.length - 1; int retval = -1; // assume target not found while (left <= right) index = (left + right) / 2; if (target == numbers[index]) retval = index; // target found if (target > numbers[index]) left = index + 1; right = index - 1; } return retval; } // method iterativebinarysearch
Recursive Binary Search The binary search algorithm can be implemented recursively It has the same general strategy as the iterative version Each recursive call narrows the search section private int recursivebinarysearch (int[] numbers, int target, int left, int right) int retval; // return value int index = (left + right) / 2; if (left > right) // target not found retval = -1; if (target == numbers[index]) // target found retval = index; if (target > numbers[index]) retval = recursivebinarysearch (numbers, target, index + 1, right); retval = recursivebinarysearch (numbers, target, left, index - 1); return retval; } // method recursivebinarysearch