常见排序算法的亲手实现(代码与注释)
1 package sort; 2 3 import java.util.ArrayList; 4 import java.util.Random; 5 6 public class Sort 7 { 8 9 public static Random r = new Random(); 10 11 12 /** 13 * 堆排序,建立小顶堆。 0-len是待调整的数组,k指向该次被调整子树的根结点 14 */ 15 16 public static void adjustDown(int arr[], int len, int k) 17 { 18 // f = l; 19 int temp = arr[k]; 20 21 for (int i = 2 * k + 1; i <= len; i = 2 * i + 1) 22 { 23 // 只有右孩子存在,且右孩子又是小的结点,那么才会改变探索指针i的方向。(i一直指向待调整子树的孩子结点中值较小的结点,为了小顶堆的目标) 24 if (i < len && arr[i] > arr[i + 1]) 25 { 26 i++; 27 } 28 if (temp <= arr[i]) 29 { 30 break;//要么break出来,要么k继续往下走 31 } 32 else 33 { 34 arr[k] = arr[i]; 35 k = i;// k一直指向,下一轮循环,待调整的子树 的父结点 36 } 37 } 38 arr[k] = temp;// 待调整的k指针没动,直接break出来,没有继续向下调整 39 } 40 41 // 堆排序,先要建立初始堆(从k到0位置,反复向下调整),然后拿出堆顶(对堆的删除),从根破坏了堆性质,又从根往下调整一次即可。 42 // 对堆的插入:新结点放在堆的末端,破坏了最下面子树的堆性质,故向上调整到某一次符合堆的性质即可 43 public static void heapSort(int arr[], int len) 44 { 45 // 这个循环建立了初始堆 46 for (int i = (len - 1) / 2; i >= 0; i--) 47 { 48 49 adjustDown(arr, len, i); 50 // Sort.disArr(arr); 51 } 52 int temp = 0; 53 for (int index = len; index >= 0;) 54 { 55 temp = arr[0]; 56 arr[0] = arr[index]; 57 arr[index] = temp; 58 // System.out.println(arr[index] + " "); 59 // Sort.disArr(arr); 60 index--; 61 adjustDown(arr, index, 0);// 固定从整个树的根开始向下调整 62 } 63 64 } 65 66 // 对小顶堆的插入,插入需要向上调整--AdjustUp 67 public static void insertHeap(int arr[], int k) 68 { 69 70 // k为插入结点的位置 71 int index = k; 72 int temp = arr[index];// n+1指向新放入的元素 73 while ((index - 1) / 2 >= 0 && temp < arr[(index - 1) / 2]) 74 { 75 arr[index] = arr[(index - 1) / 2];// 插入的元素更小,就把父节点值放到子节点中去 76 index = (index - 1) / 2;// 默认父节点值与temp交换了,一直拿temp去和父辈,父辈的父辈……去比较 77 if (index == 0) 78 break;// 不要在根兜圈圈!减1除2,在0处就不动了 79 } 80 arr[index] = temp; 81 } 82 83 // 利用向上调整,建立初始堆。 84 // 一边不断插入元素,一边自底(k)向上(0)调整一次。 85 public static void heapSort(int arr[]) 86 { 87 // 用直接插入法的思维,一个个点插入堆中,利用向上调整,建立初始堆 88 for (int i = 0; i <= arr.length - 1; i++) 89 { 90 insertHeap(arr, i); 91 } 92 93 // 还是要用到向下调整,输出序列或进行排序,因为只有堆顶元素具有“最”的特性,输出堆顶元素,从顶破坏了堆的结构,自然需要向下调整。 94 int temp = 0; 95 for (int index = arr.length - 1; index >= 0;) 96 { 97 temp = arr[0]; 98 arr[0] = arr[index]; 99 arr[index] = temp; 100 // System.out.println(arr[index] + " "); 101 // Sort.disArr(arr); 102 index--; 103 adjustDown(arr, index, 0); 104 } 105 } 106 107 public static void selectSort(int a[]) 108 { 109 int i = 0, j = 0; 110 // int restMin = 0; 111 int index = 0; 112 int temp = 0; 113 // 从第0个位置选定元素到第倒数第二个位置,最后只剩一个元素,就不用选定位置了 114 for (i = 0; i < a.length - 1; i++) 115 { 116 // restMin = a[i];//为找到最值做准备;剩下位置中的最小值 117 index = i;// 必须先初始化为当前位置,因为最值可能就是当前位置的元素嘛 118 // j指向待比较的元素 119 for (j = i + 1; j < a.length; j++) 120 { 121 // a[index]值是变化的,用index指向剩下位置中的最小值 122 if (a[j] < a[index]) 123 { 124 // restMin = a[j];//restMin保存最小值 125 index = j;// 记录最值的位置,同时a[index]自然也记录了最值的大小 126 } 127 } 128 // swap 交换最小值和当前位置元素的值 129 if (index != i) 130 { 131 temp = a[i]; 132 a[i] = a[index]; 133 a[index] = temp; 134 } 135 136 } 137 } 138 139 // 合并段中,把mid位置的放前后和后段,会影响mid取值的计算,以及边界的控制。 140 public static void merge(int a[], int left, int mid, int right) 141 { 142 if (left >= right) 143 return; 144 145 int index1 = 0;// 游标指向合并中的一段[0 到 mid-left-1],检测指针 146 int index2 = mid - left;// 游标指向合并中的另一段[mid-left- right - left] 147 int k = left;// 指向合并后序列的位置,存放指针 148 149 // 在a中取原数据, 在b上合并,再把最终结果保存回原来的数组a;或者copy数据到b,把合并后结果直接覆盖到a上 150 int b[] = new int[right - left + 1]; 151 152 for (int i = 0; i < right - left + 1; i++) 153 { 154 b[i] = a[left + i]; 155 } 156 157 while (index1 <= mid - left - 1 && index2 <= right - left) 158 { 159 if (b[index1] <= b[index2]) 160 { 161 a[k++] = b[index1++]; 162 } 163 else 164 { 165 a[k++] = b[index2++]; 166 } 167 } 168 169 while (index1 <= mid - left - 1) 170 { 171 a[k++] = b[index1++];// 没有检测完的,复制 172 } 173 while (index2 <= right - left) 174 { 175 a[k++] = b[index2++]; 176 } 177 } 178 179 public static void mergeSort(int a[], int left, int right) 180 { 181 if (left >= right) 182 return; 183 int mid = (left + right) / 2 + 1; 184 mergeSort(a, left, mid - 1); 185 mergeSort(a, mid, right); 186 merge(a, left, mid, right); 187 188 } 189 190 /** 191 * 192 * 交换排序之冒泡排序,结果升序排列 193 */ 194 public static void bubbleSort(int a[]) 195 { 196 int i = 0, j = 0, temp = 0; 197 boolean swaped = false; 198 // i,控制趟数,n个元素排序,最多进行n-1趟 199 for (i = 0; i < a.length - 1; i++) 200 { 201 swaped = false; 202 // j指向待排序序列,每一趟将一个待排序列中的最大元素放到该序列的最后。会有j+1,所以注意边界控制 203 for (j = 0; j < a.length - i - 1; j++) 204 { 205 if (a[j] > a[j + 1]) 206 { 207 temp = a[j]; 208 a[j] = a[j + 1]; 209 a[j + 1] = temp; 210 swaped = true; 211 } 212 } 213 if (swaped == false) 214 { 215 return;// 本趟无逆序,停止处理 216 } 217 } 218 } 219 220 /** 221 * 交换排序之快速排序 222 */ 223 224 225 public static int partition(int a[], int left, int right) 226 { 227 // 基准元素的选择对于快速排序性能影响较大;index 228 int index = left + r.nextInt(right - left + 1);// 随机选基准元素,把它放到a[left]哨兵位置,然后从right开始扫描,才是正确的 229 230 231 int value = a[index];// 用value保存了当前选取的枢轴元素,就可腾空一个位置 232 233 // 一定把枢轴元素交换至最左边(放到最左就从right开始检测,最右就应该从left开始检测),使得腾空的位置从最边上向枢轴的真正位置逼近!而不是一开始就从枢轴元素的起始位置开始移动 234 235 int temp = a[left]; 236 a[left] = a[index]; 237 a[index] = temp; 238 239 while (left < right) 240 { 241 // 比较中带等号,针对重复元素如:4,3,4,检测指针才会移动,不然就死循环了 242 243 // 先让右侧开始检测,对于4,9;选了9当value,直接开始right--就不对 244 while (left < right && a[right] >= value) 245 { 246 right--; 247 } 248 a[left] = a[right]; 249 250 while (left < right && a[left] <= value) 251 { 252 left++; 253 } 254 a[right] = a[left]; 255 256 } 257 // 必然left==right了 258 a[left] = value; 259 return left; 260 } 261 262 public static void quikSort(int a[], int left, int right) 263 { 264 if (left >= right) 265 return; 266 int p = partition(a, left, right); 267 quikSort(a, left, p - 1); 268 quikSort(a, p + 1, right); 269 } 270 271 /** 272 * 直接插入排序,结果为升序 273 * 274 * @param a 275 */ 276 277 //两个指针,一个指向待插入元素,一个指向已经排好序的序列 278 public static void insertSort(int a[], int left, int right) 279 { 280 int i = 0, j = 0, temp = 0; 281 // i 指向待排序的元素。实际应用时:就是传入参数left = 0,right = a.lenght-1; 282 for (i = left + 1; i <= right; i++) 283 { 284 // 如果带排序元素需要往前插入,就不断后移动元素;如果不需要,就什么不做,直接考察下一个元素 285 if (a[i] < a[i - 1]) 286 { 287 temp = a[i];// temp保存了待插入的元素 288 j = i - 1;// j指向了i之前已经有序的段 289 do 290 { 291 a[j + 1] = a[j]; 292 j--; 293 294 } while (j >= left && temp < a[j]); 295 a[j + 1] = temp;// 插入temp 296 } 297 } 298 } 299 300 public static void binaryInsertSort(int arr[],int left,int right) 301 { 302 int i=0,j=0,temp=0,high = 0,low=0,middle=0; 303 for(i=left+1;i<=right;i++) 304 { 305 temp = arr[i]; 306 low = left; 307 high = i-1; 308 while(low<=high) 309 { 310 middle = (low+high)/2; 311 if(temp < arr[middle]) 312 { 313 high = middle - 1; 314 } 315 else 316 { 317 low = middle + 1; 318 } 319 } 320 for(j=i-1;j>=low;j--) 321 { 322 arr[j+1] = arr[j]; 323 } 324 arr[low] = temp; 325 } 326 } 327 328 public static void main(String []args) 329 { 330 331 } 332 } 333