java基础算法--排序大全
1 package sorting; 2 3 import java.util.*; 4 //import java.util.Comparator; 5 //import java.util.PriorityQueue; 6 //import java.util.Queue; 7 8 public class Sorting { 9 /************************************序言**********************************************/ 10 /** 11 * 排序方法:冒泡排序,插入排序,希尔排序,堆排序(2),归并排序(2),快排(2)... 12 * */ 13 14 /** 15 * 最小值函数 16 * */ 17 private static <AnyType extends Comparable<? super AnyType>> AnyType min(AnyType a, AnyType b){ 18 if(a.compareTo(b) <= 0) 19 return a; 20 else 21 return b; 22 } 23 24 /** 25 * 交换函数 26 * */ 27 private static <AnyType extends Comparable<? super AnyType>> void swap(AnyType [] a, int m, int n){ 28 AnyType tmp = a[n]; 29 a[n] = a[m]; 30 a[m] = tmp; 31 } 32 /**********************************BubleSort*****************************************/ 33 /** 34 * 冒泡排序:BubleSort 35 * 每次内层循环最大的都被滤到最后 36 * */ 37 public static <AnyType extends Comparable<? super AnyType>> void bubleSort(AnyType [] a){ 38 for(int i=0;i<a.length;i++){ 39 for(int j=0;j<a.length-1-i;j++){ 40 if(a[j].compareTo(a[j+1]) > 0){ //如果后一个数小于前一个数交换 41 AnyType tmp=a[j]; 42 a[j]=a[j+1]; 43 a[j+1]=tmp; 44 } 45 } 46 } 47 } 48 /*************************SelectSort*************************************************/ 49 /*** 50 * 选择排序:SelectSort 51 * 每次内层循环最小的被滤到最前 52 */ 53 public static <AnyType extends Comparable<? super AnyType>> void selectSort(AnyType[] a) { 54 int minIndex; 55 for (int i = 0; i < a.length; i++) { 56 minIndex = i; 57 for (int j = i + 1; j < a.length; j++) { 58 if ((a[j].compareTo(a[minIndex])) < 0) { 59 minIndex = j; 60 } 61 } 62 swap(a, i, minIndex); 63 } 64 } 65 66 /*************************InsertionSort**********************************************/ 67 /*** 68 * 插入排序:InsertionSort 69 * @param a 70 * 插入排序的实质是从a[1]~a[a.length-1]开始,逐个比较a[p](p=1,2,...,a.length-1)与a[j-1]的值,直至找到a[p]的位置。 71 */ 72 public static <AnyType extends Comparable<? super AnyType>> void insertionSort(AnyType [] a){ 73 int j; 74 for(int p = 1; p < a.length; p++){ 75 AnyType tmp = a[p]; //务必使用tmp变量,否则可能第一轮比较过后啊a[p]也即a[j]的值被覆盖 76 for(j = p; j > 0 && tmp.compareTo(a[j - 1])<0;j--){//等于就不挪了,省一次操作 77 a[j] = a[j-1]; 78 } 79 a[j] = tmp; 80 81 } 82 } 83 /*************************ShellSort**********************************************/ 84 /** 85 * 希尔排序:ShellSort 最坏情形:O(N2) 86 * @param a 87 * 希尔排序(即间隔排序)的作用:对于间隔k,希尔排序即对k个独立的子数组的一次插入排序 88 */ 89 public static <AnyType extends Comparable<? super AnyType>> void shellSort(AnyType [] a){ 90 int j; 91 for(int gap = a.length / 2; gap > 0; gap /= 2 ){ 92 //同时对k个子数组进行间隔排序,相当于和并单独子数组排序的两个for循环 for(int i=0;i<gap;i++){for(int p=i;i<a.length;p+=gap){}} 93 for(int i = gap;i < a.length;i++){ 94 //每个子数组的插入排序 95 AnyType tmp = a[i]; 96 for(j = i;j >= gap && tmp.compareTo(a[j-gap])<0;j-=gap){//等于就不挪了,省一次操作 97 a[j] = a[j-gap]; 98 } 99 a[j] = tmp; 100 } 101 } 102 } 103 /*************************HeapSort**********************************************/ 104 /** 105 * 堆排序:HeapSort1 最坏情形:O(Nlog(N)) 堆排序要比希尔排序要慢 106 * @param a 107 * 堆排序使用优先队列java.util.PriorityQueue实现 108 */ 109 public static <AnyType extends Comparable<? super AnyType>> void heapSort1(AnyType [] a){ 110 Comparator<AnyType> comparator = new Comparator<AnyType>(){ 111 public int compare(AnyType left, AnyType right){ 112 return left.compareTo(right) ; 113 } 114 }; 115 Queue<AnyType> heap = new PriorityQueue<AnyType>(a.length,comparator); 116 for(AnyType e:a){ 117 heap.add(e); 118 } 119 int i = 0; 120 while(!heap.isEmpty()){ 121 a[i++] = heap.poll(); 122 } 123 } 124 /****************************************************************************************/ 125 /** 126 * 堆排序:HeapSort2 最坏情形:O(Nlog(N)) 127 * @param a 128 * 使用基础代码实现,建堆,排序 129 */ 130 /** 131 * 求左子节点 132 * */ 133 private static int leftChild(int i){ 134 return 2 * i + 1; 135 } 136 /** 137 * 下滤函数:deleteMin(deleteMax)时候使用 138 * 对于大根堆下滤期间,大数被逐次滤上去(一步一步),小数被一直滤到它该到的位置(for结束后) 139 * @param a 堆数组 140 * @param i 开始下滤的起点 141 * @param n 堆的有效数组长度,随着不断deleteMax操作,堆中元素会不断减少,有效数组长度n也会逐渐减小 142 */ 143 private static <AnyType extends Comparable<? super AnyType>> void percolateDown(AnyType[] a, int i, int n){ 144 int child;//左右子中较小的那个节点 145 AnyType tmp = a[i]; 146 147 for(; leftChild(i)< n; i = child){//leftChild(i)< n 判断是否到达最后一个叶子节点 148 child = leftChild(i); 149 //如果只有一个左子节点,那么不必判断那个更大了 150 if(child != n-1 && a[child].compareTo( a[child + 1] ) < 0)//child!=n-1为了确定是否有两个子节点 151 child++;//将两个儿子中大的那个滤上去 152 if(tmp.compareTo( a[child]) < 0 ){//等于就不挪了,省一次操作 153 a[i]=a[child];//大数被逐次滤上去(一步一步) 154 }else 155 break; 156 } 157 a[i]=tmp;//小数被for一直滤下来 158 } 159 160 /** 161 * 排序结果:升序 ,使用大根堆 162 * 建立大根堆,deleteMin操作,得到排序数组 163 * 下虑操作:在建立二叉堆和deleteMin中都有使用 164 * */ 165 public static <AnyType extends Comparable<? super AnyType>> void heapSort2(AnyType [] a){ 166 /* 在无序数组上直接下滤建立大根堆 167 * 从低向上开始下滤,即最后一个节点的父节点下滤即a[length/2] 168 * 堆从数组索引0开始,因此左子节点为2*i+1,右子节点为2*i+2 169 * */ 170 for(int i = a.length/2; i >= 0; i--){ 171 percolateDown(a, i, a.length); 172 } 173 /* 堆排序:不断deleteMax将堆中最大的元素放置于数组a的末端 174 * */ 175 for(int j = a.length-1; j >= 0; j--){ 176 //deleteMax 177 AnyType tmp = a[j]; 178 a[j] = a[0]; 179 //将队尾元素放置堆根处,开始下滤 180 a[0] = tmp; 181 percolateDown(a, 0, j);//初始时刻j为a.length-1 182 } 183 } 184 /*************************MergeSort**********************************************/ 185 /** 186 * 归并排序:MergeSort1 最坏运行时间O(Nlog(N)) 对空间有要求,线性内存 比较次数最少 187 * 注意: 归并排序严重依赖于比较元素和数组中移动元素的相对开销,是语言相关的。 188 * 其中:java中,执行一次泛型排序(Comparator)时 ,比较(不容易内嵌,动态调度)的的开销要大于移动元素(引用赋值);由于比较次数最少,是标准java类库中泛型排序所使用的算法。 189 * 而 C++则相反,其泛型排序中如果对象庞大,拷贝对象开销大,比较相对省时。C++库中使用快速排序方法。 190 * @param a 191 * 实现方式:递归,本质就是一直讲待排序的数组二分下去,直至每一半均只有一个元素然后依次合并,完成排序。 192 * */ 193 194 /** 195 * 实际完成归并排序的过程的程序 196 * */ 197 private static <AnyType extends Comparable<? super AnyType>> void merge(AnyType [] a, AnyType [] tmpArray, int leftPos, int rightPos, int rightEnd){ 198 int leftEnd = rightPos - 1; 199 int tmpPos = leftPos; 200 int numElements = rightEnd - leftPos + 1; 201 202 while(leftPos <= leftEnd && rightPos <= rightEnd){ 203 if(a[leftPos].compareTo(a[rightPos]) <= 0)//等不等于都得拷贝 204 tmpArray[tmpPos++] = a[leftPos++]; 205 else 206 tmpArray[tmpPos++] = a[rightPos++]; 207 } 208 while(leftPos <= leftEnd){ 209 tmpArray[tmpPos++] = a[leftPos++]; 210 } 211 while(rightPos <= rightEnd){ 212 tmpArray[tmpPos++] = a[rightPos++]; 213 } 214 for(int i = numElements; i >0; i--){ 215 a[rightEnd] = tmpArray[rightEnd]; 216 rightEnd--;//注意rightEnd要单独拿出来自减,否则在上个语句中会自减两次 217 } 218 } 219 220 /** 221 * 主递归程序 222 * */ 223 private static <AnyType extends Comparable<? super AnyType>> void mergeSort(AnyType [] a, AnyType [] tmpArray, int left, int right){ 224 if(left < right){ 225 int center = (left + right)/2; 226 mergeSort(a, tmpArray, left, center); 227 mergeSort(a, tmpArray, center + 1, right); 228 merge(a, tmpArray, left, center + 1, right);//实际完成排序过程 229 } 230 } 231 232 /** 233 * 归并排序驱动程序 234 * */ 235 public static <AnyType extends Comparable<? super AnyType>> void MergeSort1(AnyType [] a){ 236 AnyType [] tmpArray = (AnyType[]) new Comparable[a.length]; 237 mergeSort(a, tmpArray, 0, a.length - 1); 238 } 239 240 /********************************************************************************/ 241 /** 242 * 归并排序:MergeSort2 最坏运行时间O(Nlog(N)) 243 * @param a 244 * 实现方式:非递归,从单个元素开始归并合成小组,然后小组之间归并直至归并成一个完整的数组,依旧使用MergeSort1使用的merge函数 245 * */ 246 public static <AnyType extends Comparable<? super AnyType>> void MergeSort2(AnyType [] a){ 247 int n = a.length; 248 AnyType[] tmpArray = (AnyType[]) new Comparable[n]; 249 for(int subList = 1; subList < n; subList *=2){ 250 int leftPos = 0; 251 while(leftPos + subList < n){ 252 int rightPos = leftPos + subList; 253 // int leftEnd = rightPos - 1; 254 int rightEnd = min(n-1, rightPos + subList - 1); //一定要注意不能越界 min 255 merge(a, tmpArray, leftPos, rightPos, rightEnd); 256 leftPos = rightEnd + 1; //等同于leftPos += 2 * subList; 257 } 258 } 259 } 260 /*************************QuickSort**********************************************/ 261 /** 262 * 快速排序:QuickSort1 平均运行时间:O(NlogN) 最坏运行时间:O(N2) 263 * 使用三数中值分割法选取枢纽元 264 * 由于对于小数组(N<=20),快速排序的递归会不如插入排序,因此该程序调用插入排序函数。截止范围CUTOFF=10 265 * */ 266 267 268 /** 269 * 三数中值分割法:取左端,右端,中心位置的三个元素的中值作为枢纽元 270 * 实在值得注意的一点是:三数中值分割法有效的条件是left+2<=right(即至少要有三个元素)的时候才成立,对于2个及2个一下元素将会出现错误。 271 * 排序后最小的将位于a[left],最大的位于a[right],枢纽元即中间值a[center]将被放置于a[right-1](亦即交换a[center]与a[right-1]) 272 * 这样在分隔阶段,i,j将从left+1和right-2开始比较 273 * 三数中值分割法的好处:a[left]比枢纽元小,将作为j的警戒标记;而a[right-1]存放着枢纽元,则自然作为i的警戒标记。 274 * */ 275 private static <AnyType extends Comparable<? super AnyType>> AnyType median3(AnyType [] a, int left, int right){ 276 int center = (left + right)/2; 277 if(a[center].compareTo(a[left]) < 0) 278 swap(a, center, left); 279 if(a[right].compareTo(a[left]) < 0) 280 swap(a, right, left); 281 if(a[right].compareTo(a[center]) < 0) 282 swap(a, right, center); 283 //将枢纽元至于a[right - 1]的位置上 284 swap(a, center, right - 1); 285 return a[right - 1]; 286 } 287 /** 288 * 分割策略 289 * */ 290 private static <AnyType extends Comparable<? super AnyType>> int partition(AnyType [] a, int left, int right){ 291 AnyType pivot = median3(a, left, right); 292 //由于pivot放置在了a[right - 1]的位置(暂存pivot),因此i,j的取值应该为 left + 1,和right - 2 293 int i = left, j = right - 1; 294 for(;;){ 295 //在遇到i,j处值都等于pivot时候停下,但是还得继续道,j<i才算结束,因此while编写要格外注意 296 while(a[++i].compareTo(pivot) < 0){}//遇到跟pivot枢纽元值相等的值要停下,否则N2效率低下 297 while(a[--j].compareTo(pivot) > 0){} 298 if(i < j) 299 swap(a, i, j); 300 else 301 break; 302 } 303 // for(;;){ 304 // while(a[i].compareTo(pivot) < 0){i++;} 305 // while(a[j].compareTo(pivot) > 0){j--;} 306 // if(i < j){ 307 // swap(a, i, j); 308 // i++;j--; 309 // } 310 // else 311 // break; 312 // } 313 swap(a, i, right - 1); 314 return i; 315 } 316 /** 317 * 快速排序递归程序,主体程序,遇到跟pivot枢纽元值相等的值要停下 318 * 前提条件:left+1<right(即left+2<=right) 319 * 实在值得注意的一点是:三数中值分割法有效的条件是left+2<=right(即至少要有三个元素)的时候才成立,对于2个及2个一下元素将会出现错误; 320 * 而这里当CUTOFF设置为1时候,仍旧没有错误的原因是else条件下的insertionSort(a)起了作用 321 * */ 322 private static final int CUTOFF = 10;//CUTOFF>=2 323 private static <AnyType extends Comparable<? super AnyType>> void qSort(AnyType [] a, int left, int right){ 324 //利用截止范围判断数据量 325 if(left + CUTOFF <= right){ 326 //获取枢纽元 327 // AnyType pivot = median3(a, left, right); 328 // //由于pivot放置在了a[right - 1]的位置(暂存pivot),因此i,j的取值应该为 left + 1,和right - 2 329 // int i = left, j = right - 1; 330 // for(;;){ 331 // //在遇到i,j处值都等于pivot时候停下,但是还得继续道,j<i才算结束,因此while编写要格外注意 332 // while(a[++i].compareTo(pivot) < 0){}//遇到跟pivot枢纽元值相等的值要停下,否则N2效率低下 333 // while(a[--j].compareTo(pivot) > 0){} 334 // if(i < j) 335 // swap(a, i, j); 336 // else 337 // break; 338 // } 339 // for(;;){ 340 // while(a[i].compareTo(pivot) < 0){i++;} 341 // while(a[j].compareTo(pivot) > 0){j--;} 342 // if(i < j){ 343 // swap(a, i, j); 344 // i++;j--; 345 // } 346 // else 347 // break; 348 // } 349 //因为i左边都比pivot小,i及i右边除了right-1除存放着pivot外都比pivot大,因此将i处值与right-1处值交换 350 //即得到i左边都比其小,右边都比其大,i即pivot排序好的正确位置(换回pivot) 351 // swap(a, i, right - 1); 352 int i = partition(a, left, right); 353 //i已排好序 354 qSort(a, left, i - 1); 355 qSort(a, i + 1, right); 356 }else{ 357 insertionSort(a);//在这里处理了当CUTOFF等于1时的情景不会出错。当CUTOFF等于0时首先是不允许的(那样left就等于right了),其次这会导致median3()函数数组索引越界异常 358 } 359 } 360 /** 361 * 快速排序驱动程序 362 * */ 363 public static <AnyType extends Comparable<? super AnyType>> void quickSort(AnyType [] a){ 364 qSort(a, 0, a.length - 1); 365 } 366 367 /*********************************QuickSort2***************************************/ 368 /** 369 * 分割策略 370 * */ 371 private static <AnyType extends Comparable<? super AnyType>> int partition2(AnyType [] a, int start, int end){ 372 int i = start, j = end; 373 AnyType base = a[start]; 374 while(i < j ){ 375 while((a[j].compareTo(base) > 0) && (j > i)) 376 j--; 377 if(i < j){ 378 a[i] = a[j]; 379 i++;// 380 } 381 while((a[i].compareTo(base) < 0) && (i < j)) 382 i++; 383 if(i < j){ 384 a[j] = a[i]; 385 j--;// 386 } 387 388 }//while 389 a[i] = base; 390 return i; 391 } 392 /** 393 * 使用数组第一位作为枢纽元 394 * 前提条件:start<end 395 * */ 396 private static <AnyType extends Comparable<? super AnyType>> void qSort2(AnyType [] a, int start, int end){ 397 if(start < end){ 398 // int i = start, j = end; 399 // AnyType base = a[start]; 400 // while(i < j ){ 401 // while((a[j].compareTo(base) > 0) && (j > i)) 402 // j--; 403 // if(i < j){ 404 // a[i] = a[j]; 405 // i++;// 406 // } 407 // while((a[i].compareTo(base) < 0) && (i < j)) 408 // i++; 409 // if(i < j){ 410 // a[j] = a[i]; 411 // j--;// 412 // } 413 // 414 // }//while 415 // a[i] = base; 416 int i = partition2(a, start, end); 417 418 qSort2(a, start, i-1); 419 qSort2(a, i+1, end); 420 } 421 422 } 423 /** 424 * 驱动程序 425 * 使用数组第一位作为枢纽元 426 * */ 427 public static <AnyType extends Comparable<? super AnyType>> void quickSort2(AnyType [] a){ 428 qSort2(a, 0, a.length-1); 429 } 430 /*********************************QuickSort3***************************************/ 431 /** 432 * 三数中值分割的使用非递归的快速排序 433 * */ 434 public static <AnyType extends Comparable<? super AnyType>> void quickSort3(AnyType [] a){ 435 if(a==null||a.length<=0)return; 436 Stack<Integer> index=new Stack<Integer>(); 437 int start=0; 438 int end=a.length-1; 439 440 int pivotPos; 441 442 index.push(start); 443 index.push(end); 444 445 while(!index.isEmpty()){ 446 end=index.pop(); 447 start=index.pop(); 448 if(start+1<end){//三数中值必备 449 pivotPos=partition(a,start,end); 450 451 if(start<pivotPos-1){ 452 index.push(start); 453 index.push(pivotPos-1); 454 } 455 if(end>pivotPos+1){ 456 index.push(pivotPos+1); 457 index.push(end); 458 459 } 460 }else{//三数中值必备 461 if(a[start].compareTo(a[end])>0) 462 swap(a, start, end); 463 } 464 } 465 } 466 /*********************************QuickSort4***************************************/ 467 /** 468 * 使用第一值为枢纽元的使用非递归的快速排序 469 * */ 470 public static <AnyType extends Comparable<? super AnyType>> void quickSort4(AnyType [] a){ 471 if(a==null||a.length<=0)return; 472 Stack<Integer> index=new Stack<Integer>(); 473 int start=0; 474 int end=a.length-1; 475 476 int pivotPos; 477 478 index.push(start); 479 index.push(end); 480 481 while(!index.isEmpty()){ 482 end=index.pop(); 483 start=index.pop(); 484 // if(start<end){ //可有可无,因为压栈的时候已能确定start<end,此处判断是恒成立的 485 pivotPos=partition2(a,start,end); 486 487 if(start<pivotPos-1){ 488 index.push(start); 489 index.push(pivotPos-1); 490 } 491 if(end>pivotPos+1){ 492 index.push(pivotPos+1); 493 index.push(end); 494 495 } 496 // } 497 } 498 } 499 500 /*-----------------------------------------main-------------------------------------------------*/ 501 public static void main(String[] args) { 502 // TODO Auto-generated method stub 503 /** 504 * Test case: Sort the array. 505 * */ 506 // Integer[] a ={3,1,4,1,5,9,111,2,6,142,543,123,65,453,123,879,572,434,111,242,811,102}; 507 Integer[] a ={3,1,4,1,5,9,2,6}; 508 System.out.println("Before sorting:"); 509 for(Integer i:a){ 510 System.out.print(i+","); 511 } 512 System.out.println(); 513 514 Sorting.quickSort4(a);//排序方法调用 515 System.out.println("After sorting:"); 516 for(Integer i:a){ 517 System.out.print(i+","); 518 } 519 System.out.println(); 520 521 522 } 523 524 }