Sorting Algorithms Overview
Time Complexity of an algorithm is usually estimated as the asymptotic number of elementary operations with respect to an input size. When it comes to comparison-based sorting algorithms, the so-called elementary operation is usually referred to the comparison of key values.
1. Comparison-Based Sorting Algorithms
Comparison-based sorting algorithms have a lower bound of time complexity as O(n*log n).
Here I shall provide a Java program involving following sorting algorithms:
(1) Selection Sort: unstable and of quadratic time complexity
(2) Insertion Sort: stable and of quadratic time complexity
(3) Binary Insertion Sort: stable and of O(n*log n) time complexity in terms of comparisons
(4) Quick Sort: unstable and of O(n*log n) average time as long as pivot is randomly chosen
(5) Merge Sort: stable and of O(n*log n) worst-case time complexity
(6) Heap Sort: unstable and of O(n*log n) worst-case time complexity
1 class Array<T extends Comparable> { 2 private T[] items; // data of array items 3 private int len; // current length 4 private int size; // size of space 5 6 public Array(int size) { 7 if (size<=0) { 8 throw new RuntimeException("Illegal Initial Size"); 9 } else { 10 this.size = size; 11 items = (T[]) new Comparable[size]; 12 } 13 } 14 public void append(T item) { 15 // Insert a new item in items[len] 16 if (len==size) { 17 doubleSize(); 18 } 19 items[len++] = item; 20 } 21 private void doubleSize() { 22 // Double the space size when it's filled up 23 if ((size<<1)<0) { 24 throw new RuntimeException("Size Expansion Failed"); 25 } else { 26 T[] tmp = items; 27 items = (T[]) new Comparable[size<<1]; 28 for (int i=0;i<len;i++) { 29 items[i] = tmp[i]; 30 } 31 size <<= 1; 32 } 33 } 34 public void selectSort() { 35 // Implement Selection Sort Algorithm 36 for (int i=0;i<len-1;i++) { 37 int idx = i; 38 T key = items[idx]; 39 for (int j=i+1;j<len;j++) { 40 if (items[j].compareTo(key)<0) { 41 idx = j; 42 key = items[idx]; 43 } 44 } 45 items[idx] = items[i]; 46 items[i] = key; 47 } 48 } 49 public void insertSort() { 50 // Implement Insertion Sort Algorithm 51 for (int i=1;i<len;i++) { 52 T key = items[i]; 53 int j = i-1; 54 for (;j>=0&&items[j].compareTo(key)>0;j--) { 55 items[j+1] = items[j]; 56 } 57 items[j+1] = key; 58 } 59 } 60 public void binInsSort() { 61 // Implement Binary Insertion Sort 62 for (int i=1;i<len;i++) { 63 T key = items[i]; 64 int pos = binSrch(0,i-1,key); 65 for (int j=i;j>pos;j--) { 66 items[j] = items[j-1]; 67 } 68 items[pos] = key; 69 } 70 } 71 private int binSrch(int p,int r,T key) { 72 // Search for the position to insert key in itmes[p...r] 73 // Precondition: items[p...r] is a sorted array 74 // Postcondition: the position of the least item larger than 75 // key in items[p...r] if it exists, otherwise return r+1 76 if (p==r) { 77 if (items[p].compareTo(key)<=0) { 78 return p+1; 79 } else { 80 return p; 81 } 82 } else { 83 int q = (p+r)/2; 84 if (items[q+1].compareTo(key)>0) { 85 return binSrch(p,q,key); 86 } else { 87 return binSrch(q+1,r,key); 88 } 89 } 90 } 91 public void quickSort() { 92 // Implement Quick Sort Algorithm 93 genRandPerm(); 94 qsort(0,len-1); 95 } 96 private void genRandPerm() { 97 // Perturb items into a random permutation 98 Random rand = new Random(); 99 for (int i=0;i<len-1;i++) { 100 int idx = i+rand.nextInt(len-i); 101 T tmp = items[idx]; 102 items[idx] = items[i]; 103 items[i] = tmp; 104 } 105 } 106 private void qsort(int p,int r) { 107 // Divide & Conquer for quick sort 108 if (p<r) { 109 int q = partition(p,r); 110 qsort(p,q-1); 111 qsort(q+1,r); 112 } 113 } 114 private int partition(int p,int r) { 115 // Partition items[p...r] for qsort procedure 116 // Precondition: items[p...r] is a random permutation 117 // Postcondition: items[q+1...r] are larger than items[q] 118 // items[p...q-1] are smaller than or equal to items[q] 119 // pivot index q is returned 120 T key = items[r]; 121 int i = p-1; 122 for (int j=p;j<r;j++) { 123 // Loop Invariant: items[i...j-1] are larger than key 124 // items[p...i-1] are smaller than or equal to key 125 if (items[j].compareTo(key)<=0) { 126 i = i+1; 127 T tmp = items[i]; 128 items[i] = items[j]; 129 items[j] = tmp; 130 } 131 } 132 items[r] = items[i+1]; 133 items[i+1] = key; 134 return i+1; 135 } 136 public void mergeSort() { 137 // Implement Merge Sort Algorithm 138 T[] tmp = (T[]) new Comparable[len]; 139 msort(tmp,0,len-1); 140 } 141 private void msort(T[] tmp,int p,int r) { 142 // Divide & Conquer for merge sort 143 if (p<r) { 144 int q = ((p+r)>>1); 145 msort(tmp,p,q); 146 msort(tmp,q+1,r); 147 merge(tmp,p,q,r); 148 } 149 } 150 private void merge(T[] tmp,int p,int q,int r) { 151 // Merge items[p...q] and items[q+1...r] into a whole 152 // Precondition: items[p...q] and items[q+1....r] are sorted 153 // Postcondition: items[p...r] is sorted 154 for (int i=p;i<=r;i++) { 155 tmp[i] = items[i]; 156 } 157 int i = p, j = q+1, k = p; 158 while (i<=q&&j<=r) { 159 // both left[] and right[] haven't run out 160 if (tmp[i].compareTo(tmp[j])<0) { 161 items[k++] = tmp[i++]; 162 } else { 163 items[k++] = tmp[j++]; 164 } 165 } 166 while (i<=q) { 167 // only left[] hasn't run out 168 items[k++] = tmp[i++]; 169 } 170 while (j<=r) { 171 // only right[] hasn't run out 172 items[k++] = tmp[j++]; 173 } 174 } 175 public void heapsort() { 176 // Implement Heap Sort Algorithm 177 for (int i=((len-3)>>1);i>=0;i--) { 178 // build a max-heap 179 sift_down(len,i); 180 } 181 int n = len; 182 for (int i=len-1;i>0;i--) { 183 // extract the max item 184 T tmp = items[0]; 185 items[0] = items[i]; 186 items[i] = tmp; 187 sift_down(--n,0); 188 } 189 } 190 public void sift_down(int n,int i) { 191 // MAX_HEAPIFY from items[i] given n is the heap size 192 int j = (i<<1)+1, k = (i<<1)+2, idx = i; 193 if (j<n) { 194 if (items[idx].compareTo(items[j])<0) { 195 idx = j; 196 } 197 if (k<n&&items[idx].compareTo(items[k])<0) { 198 idx = k; 199 } 200 if (idx>i) { 201 T tmp = items[i]; 202 items[i] = items[idx]; 203 items[idx] = tmp; 204 sift_down(n,idx); 205 } 206 } 207 } 208 public void display() { 209 // Display the array items on the console 210 if (len>0) { 211 System.out.print(items[0]); 212 for (int i=1;i<len;i++) { 213 System.out.print(" "+items[i]); 214 } 215 } 216 System.out.println(); 217 } 218 219 }
2. Counting Sort
Since Counting Sort is not based on comparisons, its time complexity has no lower bound as those mentioned above. As an integer sorting algorithm, counting sort takes O(N) time, where N is the range of the input integers (MAX - MIN).
Talk is cheap, show you my code. ╮( ̄. ̄)╭
1 public static void countSort(int[] arr) { 2 int n = arr.length; 3 int min = arr[0], max = arr[0]; 4 for (int i=1;i<n;i++) { 5 // Determine the minimum and maximum values 6 if (min>arr[i]) { 7 min = arr[i]; 8 } else if (max<arr[i]){ 9 max = arr[i]; 10 } 11 } 12 max -= min; 13 int[] cnt = new int[max+1]; 14 for (int i=0;i<n;i++) { 15 // Count the frequency of each value in arr[] 16 cnt[arr[i]-min]++; 17 } 18 for (int i=1;i<=max;i++) { 19 // Determine the starting position of each value 20 cnt[i] += cnt[i-1]; 21 } 22 int pos = 0; 23 for (int i=0;i<=max;i++) { 24 // Put the values in their proper positions 25 while (cnt[i]>pos) { 26 arr[pos++] = i+min; 27 } 28 } 29 }
3. Applications of Sorting Algorithms
We can apply the sorting algorithms described above to many other problems. For instance, we can select a certain Order Statistic of an integer array by drawing on the partition method in Quick Sort:
1 public static int select(int[] arr,int idx) { 2 // Determine the idxth Order Statistic in arr[] 3 // Precondition: 1<=idx<=arr.length 4 // Postcondition: the value desired is returned 5 int n = arr.length; 6 int[] tmp = new int[n]; 7 for (int i=0;i<n;i++) { 8 tmp[i] = arr[i]; 9 } 10 return selectHelp(tmp,0,n-1,idx); 11 } 12 private static int selectHelp(int[] arr,int p,int r,int idx) { 13 // Determine the idxth Order Statistic among arr[p...r] 14 // Precondition: 1<=idx<=r+1-p (p<=r) 15 // Postcondition: the value desired is returned 16 if (p==r) { 17 return arr[p]; 18 } 19 int key = arr[r]; 20 int q = p; 21 for (int j=p;j<r;j++) { 22 // arr[p...q-1]<=key<arr[q...j] 23 if (arr[j]<=key) { 24 int tmp = arr[q]; 25 arr[q] = arr[j]; 26 arr[j] = tmp; 27 q++; 28 } 29 } 30 arr[r] = arr[q]; 31 arr[q] = key; 32 if (idx<=q-p) { 33 return selectHelp(arr,p,q-1,idx); 34 } else { 35 return selectHelp(arr,q,r,idx+p-q); 36 } 37 }
Another example is that we use the merge method in Merge Sort to count the number of inversions in a given integer array:
1 public static int invNum(int[] arr) { 2 int n = arr.length; 3 int[] tmp = new int[n]; 4 for (int i=0;i<n;i++) { 5 tmp[i] = arr[i]; 6 } 7 int[] left = new int[(n-1)/2+2]; 8 int[] right = new int[n/2+1]; 9 return invNumHelp(tmp,0,n-1,left,right); 10 } 11 private static int invNumHelp(int[] arr,int p,int r,int[] left,int[] right) { 12 // Determine the number of inversions in arr[p...r] 13 // and meanwhile turn it into increasing order 14 if (p==r) { 15 return 0; 16 } 17 int q = ((p+r)>>1), val = 0; 18 // solve subproblems recursively 19 val += invNumHelp(arr,p,q,left,right); 20 val += invNumHelp(arr,q+1,r,left,right); 21 // copy arr[] to left[] and right[] 22 for (int i=p;i<=q;i++) { 23 left[i-p] = arr[i]; 24 } 25 for (int i=q+1;i<=r;i++) { 26 right[i-q-1] = arr[i]; 27 } 28 // set sentinels in left[] and right[] 29 left[q+1-p] = (1<<31)-1; 30 right[r-q] = (1<<31)-1; 31 // count inversions across left and right 32 for (int i=0,j=0,k=p;k<=r;k++) { 33 if (left[i]>right[j]) { 34 arr[k] = right[j++]; 35 val += (q+1-p-i); 36 } else { 37 arr[k] = left[i++]; 38 } 39 } 40 return val; 41 }
As a matter of fact, Quick Sort is a Divide-and-Conquer Algorithm that solves the problem in a top-down way, while Merge Sort adopts a bottom-up approach instead.
References:
1. Cormen, T. H. et al. Introduction to Algorithms [M] . 北京:机械工业出版社, 2006-09