排序算法的重要性不言而喻,为了加深对这十种算法的理解,固写此文。
十大经典排序算法 | 平均时间复杂度 | 最好时间复杂度 | 最坏时间复杂度 | 空间复杂度 | 排序方式 | 稳定性 |
---|---|---|---|---|---|---|
冒泡排序 | O \Omicron O(n2) | O \Omicron O(n) | O \Omicron O(n2) | O \Omicron O(1) | In-place | 稳定 |
选择排序 | O \Omicron O(n2) | O \Omicron O(n2) | O \Omicron O(n2) | O \Omicron O(1) | In-place | 不稳定 |
插入排序 | O \Omicron O(n2) | O \Omicron O(n) | O \Omicron O(n2) | O \Omicron O(1) | In-place | 稳定 |
希尔排序 | O \Omicron O(n1.3) | O \Omicron O(n) | O \Omicron O(n2) | O \Omicron O(1) | In-place | 不稳定 |
归并排序 | O \Omicron O(n log \log log2n) | O \Omicron O(n log \log log2n) | O \Omicron O(n log \log log2n) | O \Omicron O(n) | Out-place | 稳定 |
快速排序 | O \Omicron O(n log \log log2n) | O \Omicron O(n log \log log2n) | O \Omicron O(n2) | O \Omicron O( log \log log2n) | In-place | 不稳定 |
堆排序 | O \Omicron O(n log \log log2n) | O \Omicron O(n log \log log2n) | O \Omicron O(n log \log log2n) | O \Omicron O(1) | In-place | 不稳定 |
计数排序 | O \Omicron O(n+k) | O \Omicron O(n+k) | O \Omicron O(n+k) | O \Omicron O(k) | Out-place | 稳定 |
桶排序 | O \Omicron O(n+k) | O \Omicron O(n+k) | O \Omicron O(n2) | O \Omicron O(n+k) | Out-place | 稳定 |
基数排序 | O \Omicron O(n*k) | O \Omicron O(n*k) | O \Omicron O(n*k) | O \Omicron O(n+k) | Out-place | 稳定 |
表中数据说明: |
该十种排序算法可分为如下所示的两大类
算法步驟
代码实现
public class BubbleSort {
public static void bubbleSort(int[] arr) {
int len = arr.length;
for (int i = 0; i < len - 1; i++) {
boolean flag = true;
for (int j = 0; j < len - i - 1; j++) {
if (arr[j] > arr[j + 1]) {
int tmp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = tmp;
flag = false;
}
}
if (flag) {
break;
}
}
}
}
算法步驟
代码实现
public class SelectionSort {
public static void selectionSort(int[] arr) {
int len = arr.length;
for (int i = 0; i < len - 1; i++) {
int minVal = i;
for (int j = i + 1; j < len; j++) {
if (arr[minVal] > arr[j]) {
minVal = j;
}
}
if (minVal != i) {
int tmp = arr[i];
arr[i] = arr[minVal];
arr[minVal] = tmp;
}
}
}
}
算法步驟
代码实现
public class InsertionSort {
public static void insertionSort(int[] arr) {
for (int i = 1; i < arr.length; i++) {
int val = arr[i], j = i;
while (j > 0 && val < arr[j - 1]) {
arr[j] = arr[j - 1];
j--;
}
arr[j] = val;
}
}
}
算法步驟
其中,增量gap=length/2,缩小增量继续以gap = gap/2的方式,这种增量选择我们可以用一个序列来表示,{n/2, (n/2)/2, …, 1},称为增量序列。一般的增量序列都选择以上说明的这个,但不一定是最优的。
代码实现
public class ShellSort {
public static void shellSort(int[] arr) {
int len = arr.length, tmp, j;
for (int gap = len / 2; gap >= 1; gap = gap / 2) {
for (int i = gap; i < len; i++) {
tmp = arr[i];
j = i - gap;
while (j >= 0 && arr[j] > tmp) {
arr[j + gap] = arr[j];
j -= gap;
}
arr[j + gap] = tmp;
}
}
}
}
算法步驟
代码实现
import java.util.Arrays;
public class MergeSort {
public static int[] mergeSort(int[] arr) {
int len = arr.length;
if (len < 2) {
return arr;
}
int mIdx = len / 2;
return merge(mergeSort(Arrays.copyOfRange(arr, 0, mIdx)), mergeSort(Arrays.copyOfRange(arr, mIdx, len)));
}
private static int[] merge(int[] arrLeft, int[] arrRight) {
int leftLen = arrLeft.length, rightLen = arrRight.length, leftIdx = 0, rightIdx = 0, idx = 0;
int[] result = new int[leftLen + rightLen];
while (leftIdx < leftLen && rightIdx < rightLen) {
if (arrLeft[leftIdx] < arrRight[rightIdx]) {
result[idx++] = arrLeft[leftIdx++];
} else {
result[idx++] = arrRight[rightIdx++];
}
}
while (leftIdx < leftLen) {
result[idx++] = arrLeft[leftIdx++];
}
while (rightIdx < rightLen) {
result[idx++] = arrRight[rightIdx++];
}
return result;
}
}
算法步驟
代码实现
public class QuickSort {
public static void quickSort(int[] arr) {
sort(arr, 0, arr.length - 1);
}
private static void sort(int[] arr, int left, int right) {
if (left < right) {
int pivotIdx = partition(arr, left, right);
sort(arr, 0, pivotIdx - 1);
sort(arr, pivotIdx + 1, right);
}
}
private static int partition(int[] arr, int left, int right) {
int idx = left + 1;
for (int i = idx; i <= right; i++) {
if (arr[left] > arr[i]) {
swap(arr, i, idx++);
}
}
swap(arr, left, idx - 1);
return idx - 1;
}
private static void swap(int[] arr, int idx1, int idx2) {
int tmp = arr[idx1];
arr[idx1] = arr[idx2];
arr[idx2] = tmp;
}
}
算法步驟
代码实现
public class HeapSort {
private static int heapLen;
public static void heapSort(int[] arr) {
heapLen = arr.length;
for (int i = heapLen - 1; i >= 0; i--) {
heapify(arr, i);
}
for (int i = heapLen - 1; i > 0; i--) {
swap(arr, 0, heapLen - 1);
heapLen--;
heapify(arr, 0);
}
}
private static void heapify(int[] arr, int idx) {
int left = idx * 2 + 1, right = idx * 2 + 2, largest = idx;
if (left < heapLen && arr[left] > arr[largest]) {
largest = left;
}
if (right < heapLen && arr[right] > arr[largest]) {
largest = right;
}
if (largest != idx) {
swap(arr, largest, idx);
heapify(arr, largest);
}
}
private static void swap(int[] arr, int idx1, int idx2) {
int tmp = arr[idx1];
arr[idx1] = arr[idx2];
arr[idx2] = tmp;
}
}
算法步驟
代码实现
public class CountingSort {
public static void countingSort(int[] arr) {
int len = arr.length;
if (len < 2) return;
int minVal = arr[0], maxVal = arr[0];
for (int i = 1; i < len; i++) {
if (arr[i] < minVal) {
minVal = arr[i];
} else if (arr[i] > maxVal) {
maxVal = arr[i];
}
}
int[] countArr = new int[maxVal - minVal + 1];
for (int val : arr) {
countArr[val - minVal]++;
}
for (int arrIdx = 0, countIdx = 0; countIdx < countArr.length; countIdx++) {
while (countArr[countIdx]-- > 0) {
arr[arrIdx++] = minVal + countIdx;
}
}
}
}
算法步驟
代码实现
import java.util.ArrayList;
import java.util.List;
public class BucketSort {
private static List<Integer> bucketSort(List<Integer> arr, int bucketSize) {
int len = arr.size();
if (len < 2 || bucketSize == 0) {
return arr;
}
int minVal = arr.get(0), maxVal = arr.get(0);
for (int i = 1; i < len; i++) {
if (arr.get(i) < minVal) {
minVal = arr.get(i);
} else if (arr.get(i) > maxVal) {
maxVal = arr.get(i);
}
}
int bucketNum = (maxVal - minVal) / bucketSize + 1;
List<List<Integer>> bucket = new ArrayList<>();
for (int i = 0; i < bucketNum; i++) {
bucket.add(new ArrayList<>());
}
for (int val : arr) {
int idx = (val - minVal) / bucketSize;
bucket.get(idx).add(val);
}
for (int i = 0; i < bucketNum; i++) {
if (bucket.get(i).size() > 1) {
bucket.set(i, bucketSort(bucket.get(i), bucketSize / 2));
}
}
List<Integer> result = new ArrayList<>();
for (List<Integer> val : bucket) {
result.addAll(val);
}
return result;
}
}
算法步骤
import java.util.ArrayList;
import java.util.List;
//基数排序
public class RadixSort {
public static void radixSort(int[] arr) {
if (arr.length < 2) return;
int maxVal = arr[0];//求出最大值
for (int a : arr) {
if (maxVal < a) {
maxVal = a;
}
}
int n = 1;
while (maxVal / 10 != 0) {//求出最大值位数
maxVal /= 10;
n++;
}
for (int i = 0; i < n; i++) {
List<List<Integer>> radix = new ArrayList<>();
for (int j = 0; j < 10; j++) {
radix.add(new ArrayList<>());
}
int index;
for (int a : arr) {
index = (a / (int) Math.pow(10, i)) % 10;
radix.get(index).add(a);
}
index = 0;
for (List<Integer> list : radix) {
for (int a : list) {
arr[index++] = a;
}
}
}
}
}
数据量规模较小,考虑插入或选择。当元素分布有序时插入将大大减少比较和移动记录的次数,如果不要求稳定性,可以使用选择,效率略高于插入;
数据量规模中等,使用希尔排序;
数据量规模较大,考虑堆排序(元素分布接近正序或逆序)、快速排序(元素分布随机)和归并排序(稳定性);
一般来说不使用冒泡。