Divide-and-Conquer Algorithm Divide-and-conquer approach used divide the big problem into two smaller problems and solve each one separately. The solution to each smaller problem is the same: You divide it into two even smaller problems and solve them. The process continues until you get to the base case, which can be solved easily, with no further division into halves. The divide-and-conquer approach is commonly used with recursion. Quicksort Quicksort is a fast sorting algorithm, which is used not only for educational purposes, but widely applied in practice. On the average, it has O(n log n) complexity, making quicksort suitable for sorting big data volumes. The idea of the algorithm is quite simple and once you realize it. The Quicksort algorithm is based on a simple but clever idea: Given a list of items, Select any item from the list. This item is called the pivot. Move all the items that are smaller than the pivot to the beginning of the list, and move all the items that are larger than the pivot to the end of the list. Now, put the pivot between the two groups of items. This puts the pivot in the position that it will occupy in the final, completely sorted array. It will not have to be moved again. Quicksort was discovered by C.A.R. Hoare in 1962. The divide-and-conquer strategy is used in quicksort. Below the recursion steps are described: 1. Choose a pivot value. We take the value of the middle element as pivot value (or the first item in the left, the last item in the right), but it can be any value, which is in range of sorted values, even if it doesn't present in the array. 2. Partition. Rearrange elements in such a way, that all elements which are lesser than the pivot go to the left part of the array and all elements greater than the pivot, go to the right part of the array. Values equal to the pivot can stay in any part of the array. Notice that array may be divided in non-equal parts. 3. Sort both parts. Apply quicksort algorithm recursively to the left and the right parts. Partition Algorithm In Detail There are two indices i and j, at the very beginning of the partition algorithm i points to the first element in the array and j points to the last one. Then algorithm moves i forward, until an element with value greater or equal to the pivot is found. Index j is moved backward, until an element with value lesser or equal to the pivot is found. If i ≤ j then they are swapped and i steps to the next position (i + 1), j steps to the previous one (j - 1). Algorithm stops, when i become greater than j. After partition, all values before i-th element are less or equal than the pivot and all values after j-th element are greater or equal to the pivot. 35 As you can see, there are three basic steps: 1. Partition the array or subarray into left (smaller keys) and right (larger keys) groups. 2. Call Quicksort to sort the left group. 3. Call Quicksort again to sort the right group. Quicksort Algorithm Given an array of n elements (e.g., integers): • If array only contains one element, return • Else – Pick one element to use as pivot. – Partition elements into two sub-arrays: • Elements less than or equal to pivot • Elements greater than pivot – Quicksort two sub-arrays – Return results The pseudo code of the algorithm is as follows: To sort a[left...right]: if left < right: Partition a[left...right] such that: all a[left...p-1] are less than a[p], and all a[p+1...right] are >= a[p] Quicksort a[left...p-1] Quicksort a[p+1...right] Terminate A key step in the Quicksort algorithm is partitioning the array – We choose some (any) number p in the array to use as a pivot – We partition the array into three parts: – To partition a[left...right]: Set p = a[left], l = left + 1, r = right; while l < r, do while l < right && a[l] < p { l = l + 1 } while r > left && a[r] >= p { r = r – 1} if l < r { swap a[l] and a[r] } a[left] = a[r]; a[r] = p; Terminate To see what is going on, consider the example of the following array: 35 Pick Pivot Element There are a number of ways to pick the pivot element. In this example, we will use the first element in the array: Partitioning Array Given a pivot, partition the elements of the array such that the resulting array consists of: 1. One sub-array that contains elements >= pivot 2. Another sub-array that contains elements < pivot The sub-arrays are stored in the original data array. Partitioning loops through, swapping elements below/above pivot. 1. While data[too_big_index] <= data[pivot] ++too_big_index 2. While data[too_small_index] > data[pivot] --too_small_index 3. If too_big_index < too_small_index swap data[too_big_index] and data[too_small_index] 33 4. While too_small_index > too_big_index, go to 1. 1. While data[too_big_index] <= data[pivot] ++too_big_index 2. While data[too_small_index] > data[pivot] --too_small_index 3. If too_big_index < too_small_index swap data[too_big_index] and data[too_small_index] 4. While too_small_index > too_big_index, go to 1. 2. While data[too_small_index] > data[pivot] --too_small_index 3. If too_big_index < too_small_index swap data[too_big_index] and data[too_small_index] 35 4. While too_small_index > too_big_index, go to 1. Return to step 1 Then step 2 Step 3 Step 4 Swap data[too_small_index] and data[pivot_index] Partition Result The next steps Apply Quicksort algorithm recursively to the left and the right parts. 35 Example 3 7 8 5 2 1 9 5 4 3 7 8 5 2 1 9 5 4 3 1 8 5 2 7 9 5 4 3 1 2 5 8 7 9 5 4 3 1 2 5 8 7 9 5 4 3 1 2 4 8 7 9 5 5 3 1 2 4 8 7 9 5 5 3 1 2 1 3 2 1 2 3 Radix Sort The radix sort disassembles the key into digits and arranges the data items according to the value of the digits. Amazingly, no comparisons are necessary. Algorithm for the Radix Sort We’ll discuss the radix sort in terms of normal base-10 arithmetic, which is easier to visualize. However, an efficient implementation of the radix sort would use base-2 arithmetic to take advantage of the computer’s speed in bit manipulation. We’ll look at the radix sort rather than the similar but somewhat more complex radix-exchange sort. The word radix means the base of a system of numbers. Ten is the radix of the decimal system and 2 is the radix of the binary system. The sort involves examining each digit of the key separately, starting with the 1s (least significant) digit. 1. All the data items are divided into 10 groups, according to the value of their 1s digit. 2. These 10 groups are then reassembled: All the keys ending with 0 go first, followed by all the keys ending in 1, and so on up to 9. We’ll call these steps a sub-sort. 3. In the second sub-sort, all data is divided into 10 groups again, but this time according to the value of their 10s digit. This must be done without changing the order of the previous sort. That is, within each of the 10 groups, the ordering of the items remains the same as it was after step 2; the sub-sorts must be stable. 35 4. Again the 10 groups are recombined, those with a 10s digit of 0 first, then those with a 10s digit of 1, and so on up to 9. 5. This process is repeated for the remaining digits. If some keys have fewer digits than others, their higher-order digits are considered to be 0. Here’s an example, using seven data items, each with three digits. Leading zeros are shown for clarity. 421 240 035 532 305 430 124 // unsorted array (240 430) (421) (532) (124) (035 305) // sorted on 1s digit (305) (421 124) (430 532 035) (240) // sorted on 10s digit (035) (124) (240) (305) (421 430) (532) // sorted on 100s digit 035 124 240 305 421 430 532 // sorted array The parentheses delineate the groups. Within each group the digits in the appropriate position are the same. To convince yourself that this approach really works, try it on a piece of paper with some numbers you make up. The radix-sort algorithm sorts a sequence S of entries with keys that are pairs, by applying a stable bucket-sort on the sequence twice; first using one component of the pair as the ordering key and then using the second component. But which order is correct? Example: Original, unsorted list: 170, 045, 075,090, 002, 024, 802, 066 The first counting pass starts on the least significant digit of each key, producing an array of bucket sizes: 2 (bucket size for digits of 0: 170, 090) 2 (bucket size for digits of 2: 002, 802) 1 (bucket size for digits of 4: 024) 2 (bucket size for digits of 5: 045, 075) 1 (bucket size for digits of 6: 066) A second counting pass on the next more significant digit of each key will produce an array of bucket sizes: 2 (bucket size for digits of 0: 002, 802) 1 (bucket size for digits of 2: 024) 1 (bucket size for digits of 4: 045) 1 (bucket size for digits of 6: 066) 2 (bucket size for digits of 7: 170, 075) 1 (bucket size for digits of 9: 090) A third and final counting pass on the most significant digit of each key will produce an array of bucket sizes: 6 (bucket size for digits of 0: 002, 024, 045, 066, 075, 090) 1 (bucket size for digits of 1: 170) 1 (bucket size for digits of 8: 802) 35