常见排序算法的 Java 实现
https://www.cnblogs.com/shixiangwan/p/6724292.html
本文主要记录了几种常见的排序算法的 Java 实现,如冒泡排序
、快速排序
、直接插入排序
、希尔排序
、选择排序
等等。
1. 冒泡排序 将序列中所有元素两两比较,将最大的放在最后面。
将剩余序列中所有元素两两比较,将最大的放在最后面。
重复第二步,直到只剩下一个数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 private static void bubbleSort (int [] arr) { for (int i = 0 ; i < arr.length-1 ; i++) { for (int j = 0 ; j < arr.length - 1 - i; j++) { if (arr[j] > arr[j + 1 ]) { int temp = arr[j]; arr[j] = arr[j + 1 ]; arr[j + 1 ] = temp; } } } }
2. 快速排序 快速排序(Quicksort)是对冒泡排序的一种改进,借用了分治的思想,由 C. A. R. Hoare 在 1962 年提出。它的基本思想是:通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列.
具体步骤
快速排序使用分治策略来把一个序列(list)分为两个子序列(sub-lists)。
①. 从数列中挑出一个元素,称为” 基准”(pivot)。
②. 重新排序数列,所有比基准值小的元素摆放在基准前面,所有比基准值大的元素摆在基准后面(相同的数可以到任一边)。在这个分区结束之后,该基准就处于数列的中间位置。这个称为分区(partition)操作。
③. 递归地(recursively)把小于基准值元素的子数列和大于基准值元素的子数列排序。
递归到最底部时,数列的大小是零或一,也就是已经排序好了。这个算法一定会结束,因为在每次的迭代(iteration)中,它至少会把一个元素摆到它最后的位置去。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 public static void quickSort (int [] arr, int start, int end) { if (start < end) { int baseNum = arr[start]; int midNum; int left = start; int right = end; while (left<right){ while ((arr[left] < baseNum) && left < end) { left++; } while ((arr[right] > baseNum) && right > start) { right--; } if (left <= right) { midNum = arr[left]; arr[left] = arr[right]; arr[right] = midNum; left++; right--; } } if (start < right) { quickSort(arr, start, right); } if (end > left) { quickSort(arr, left, end); } } }
3. 插入排序 直接插入排序(Straight Insertion Sorting)的基本思想:将数组中的所有元素依次跟前面已经排好的元素相比较,如果选择的元素比已排序的元素小,则交换,直到全部元素都比较过为止。
首先设定插入次数,即循环次数,for(int i=1;i<length;i++),1 个数的那次不用插入。
设定插入数和得到已经排好序列的最后一个数的位数。insertNum 和 j=i-1。
从最后一个数开始向前循环,如果插入数小于当前数,就将当前数向后移动一位。
将当前数放置到空着的位置,即 j+1。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 public static void straightInsertion (int [] arr) { int current; for (int i = 1 ; i < arr.length; i++) { current = arr[i]; int j = i - 1 ; while (j >= 0 && arr[j] > current) { arr[j + 1 ] = arr[j]; j--; } arr[j + 1 ] = current; } }
4. 希尔排序 是插入排序的一种高速而稳定的改进版本。
希尔排序是先将整个待排序的记录序列分割成为若干子序列分别进行直接插入排序,待整个序列中的记录 “基本有序” 时,再对全体记录进行依次直接插入排序。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 public static void shellSort (int [] arr) { int gap = arr.length / 2 ; for (; gap > 0 ; gap = gap / 2 ) { for (int j = 0 ; (j + gap) < arr.length; j++) { for (int k = 0 ; (k + gap) < arr.length; k += gap) { if (arr[k] > arr[k + gap]) { int temp = arr[k]; arr[k] = arr[k + gap]; arr[k + gap] = temp; } } } } }
5. 选择排序 遍历整个序列,将最小的数放在最前面。
遍历剩下的序列,将最小的数放在最前面。
重复第二步,直到只剩下一个数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 public static void selectSort (int [] arr) { for (int i = 0 ; i < arr.length; i++) { int min = arr[i]; int index = i; for (int j = i + 1 ; j < arr.length; j++) { if (arr[j] < min) { min = arr[j]; index = j; } } arr[index] = arr[i]; arr[i] = min; } }
6. 堆排序 对简单选择排序的优化。
将序列构建成大顶堆。
将根节点与最后一个节点交换,然后断开最后一个节点。
重复第一、二步,直到所有节点断开。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 public void heapSort (int [] a) { int len=a.length; for (int i=0 ;i<len-1 ;i++){ buildMaxHeap(a,len-1 -i); swap(a,0 ,len-1 -i); } } private void swap (int [] data, int i, int j) { int tmp=data[i]; data[i]=data[j]; data[j]=tmp; } private void buildMaxHeap (int [] data, int lastIndex) { for (int i=(lastIndex-1 )/2 ;i>=0 ;i--){ int k=i; while (k*2 +1 <=lastIndex){ int biggerIndex=2 *k+1 ; if (biggerIndex<lastIndex){ if (data[biggerIndex]<data[biggerIndex+1 ]){ biggerIndex++; } } if (data[k]<data[biggerIndex]){ swap(data,k,biggerIndex); k=biggerIndex; }else { break ; } } } }
7. 归并排序 速度仅次于快速排序,内存少的时候使用,可以进行并行计算的时候使用。
选择相邻两个数组成一个有序序列。
选择相邻的两个有序序列组成一个有序序列。
重复第二步,直到全部组成一个有序序列。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 public void mergeSort (int [] a, int left, int right) { int t = 1 ; int size = right - left + 1 ; while (t < size) { int s = t; t = 2 * s; int i = left; while (i + (t - 1 ) < size) { merge(a, i, i + (s - 1 ), i + (t - 1 )); i += t; } if (i + (s - 1 ) < right) merge(a, i, i + (s - 1 ), right); } } private static void merge (int [] data, int p, int q, int r) { int [] B = new int [data.length]; int s = p; int t = q + 1 ; int k = p; while (s <= q && t <= r) { if (data[s] <= data[t]) { B[k] = data[s]; s++; } else { B[k] = data[t]; t++; } k++; } if (s == q + 1 ) B[k++] = data[t++]; else B[k++] = data[s++]; for (int i = p; i <= r; i++) data[i] = B[i]; }
8. 基数排序 用于大量数,很长的数进行排序时。
将所有的数的个位数取出,按照个位数进行排序,构成一个序列。
将新构成的所有的数的十位数取出,按照十位数进行排序,构成一个序列。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 public void baseSort (int [] a) { int max = a[0 ]; for (int i = 1 ; i < a.length; i++) { if (a[i] > max) { max = a[i]; } } int time = 0 ; while (max > 0 ) { max /= 10 ; time++; } List<ArrayList<Integer>> queue = new ArrayList <ArrayList<Integer>>(); for (int i = 0 ; i < 10 ; i++) { ArrayList<Integer> queue1 = new ArrayList <Integer>(); queue.add(queue1); } for (int i = 0 ; i < time; i++) { for (int j = 0 ; j < a.length; j++) { int x = a[j] % (int ) Math.pow(10 , i + 1 ) / (int ) Math.pow(10 , i); ArrayList<Integer> queue2 = queue.get(x); queue2.add(a[j]); queue.set(x, queue2); } int count = 0 ; for (int k = 0 ; k < 10 ; k++) { while (queue.get(k).size() > 0 ) { ArrayList<Integer> queue3 = queue.get(k); a[count] = queue3.get(0 ); queue3.remove(0 ); count++; } } } }
排序法 平均时间 最小时间 最大时间 稳定度 额外空间 备注 冒泡排序 O(n2) O(n) O(n2) 稳定 O(1) n 小时较好 选择排序 O(n2) O(n2) O(n2) 不稳定 O(1) n 小时较好 插入排序 O(n2) O(n) O(n2) 稳定 O(1) 大部分已排序时较好 基数排序 O(logRB) O(n) O(logRB) 稳定 O(n) B 是真数 (0-9),R 是基数 (个十百) Shell 排序 O(nlogn) - O(ns) 不稳定 O(1) s 是所选分组 快速排序 O(nlogn) O(n2) O(n2) 不稳定 O(logn) n 大时较好 归并排序 O(nlogn) O(nlogn) O(nlogn) 稳定 O(n) 要求稳定性时较好 堆排序 O(nlogn) O(nlogn) O(nlogn) 不稳定 O(1) n 大时较好