以下是 Java 中常见排序算法的完整实现,包含详细注释和时间复杂度分析:
import java.util.Arrays;
import java.util.Collections;
public class SortingAlgorithms {
public static void main(String[] args) {
int[] arr = {64, 34, 25, 12, 22, 11, 90};
System.out.println("原始数组: " + Arrays.toString(arr));
// 测试不同排序算法
testSort("冒泡排序", SortingAlgorithms::bubbleSort, arr.clone());
testSort("选择排序", SortingAlgorithms::selectionSort, arr.clone());
testSort("插入排序", SortingAlgorithms::insertionSort, arr.clone());
testSort("希尔排序", SortingAlgorithms::shellSort, arr.clone());
testSort("归并排序", SortingAlgorithms::mergeSort, arr.clone());
testSort("快速排序", SortingAlgorithms::quickSort, arr.clone());
testSort("堆排序", SortingAlgorithms::heapSort, arr.clone());
testSort("计数排序", SortingAlgorithms::countingSort, arr.clone());
testSort("桶排序", SortingAlgorithms::bucketSort, arr.clone());
testSort("基数排序", SortingAlgorithms::radixSort, arr.clone());
testSort("Java内置排序", Arrays::sort, arr.clone());
}
private static void testSort(String name, SortFunction sortFunction, int[] arr) {
long startTime = System.nanoTime();
sortFunction.sort(arr);
long endTime = System.nanoTime();
System.out.printf("%-12s: %s \t耗时: %d ns%n",
name, Arrays.toString(arr), (endTime - startTime));
}
// 排序函数接口
interface SortFunction {
void sort(int[] arr);
}
// 1. 冒泡排序 - O(n²)
public static void bubbleSort(int[] arr) {
int n = arr.length;
for (int i = 0; i < n - 1; i++) {
boolean swapped = false; // 优化:检查是否已有序
for (int j = 0; j < n - i - 1; j++) {
if (arr[j] > arr[j + 1]) {
// 交换相邻元素
int temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
swapped = true;
}
}
// 如果本轮没有交换,说明已有序
if (!swapped) break;
}
}
// 2. 选择排序 - O(n²)
public static void selectionSort(int[] arr) {
int n = arr.length;
for (int i = 0; i < n - 1; i++) {
int minIdx = i;
// 找到未排序部分的最小元素
for (int j = i + 1; j < n; j++) {
if (arr[j] < arr[minIdx]) {
minIdx = j;
}
}
// 将最小元素交换到已排序部分末尾
int temp = arr[minIdx];
arr[minIdx] = arr[i];
arr[i] = temp;
}
}
// 3. 插入排序 - O(n²) (对小数据集高效)
public static void insertionSort(int[] arr) {
int n = arr.length;
for (int i = 1; i < n; i++) {
int key = arr[i];
int j = i - 1;
// 将大于key的元素后移
while (j >= 0 && arr[j] > key) {
arr[j + 1] = arr[j];
j--;
}
arr[j + 1] = key;
}
}
// 4. 希尔排序 - O(n log n) 到 O(n²)
public static void shellSort(int[] arr) {
int n = arr.length;
// 动态计算初始间隔 (Knuth序列)
int gap = 1;
while (gap < n / 3) {
gap = 3 * gap + 1;
}
while (gap > 0) {
// 使用插入排序对每个间隔序列排序
for (int i = gap; i < n; i++) {
int temp = arr[i];
int j = i;
while (j >= gap && arr[j - gap] > temp) {
arr[j] = arr[j - gap];
j -= gap;
}
arr[j] = temp;
}
gap /= 3; // 缩小间隔
}
}
// 5. 归并排序 - O(n log n)
public static void mergeSort(int[] arr) {
mergeSort(arr, 0, arr.length - 1);
}
private static void mergeSort(int[] arr, int left, int right) {
if (left < right) {
int mid = left + (right - left) / 2;
// 递归排序左右子数组
mergeSort(arr, left, mid);
mergeSort(arr, mid + 1, right);
// 合并已排序的子数组
merge(arr, left, mid, right);
}
}
private static void merge(int[] arr, int left, int mid, int right) {
// 创建临时数组
int[] temp = new int[right - left + 1];
int i = left, j = mid + 1, k = 0;
// 合并两个子数组
while (i <= mid && j <= right) {
if (arr[i] <= arr[j]) {
temp[k++] = arr[i++];
} else {
temp[k++] = arr[j++];
}
}
// 复制剩余元素
while (i <= mid) {
temp[k++] = arr[i++];
}
while (j <= right) {
temp[k++] = arr[j++];
}
// 复制回原数组
System.arraycopy(temp, 0, arr, left, temp.length);
}
// 6. 快速排序 - O(n log n) 平均, O(n²) 最坏
public static void quickSort(int[] arr) {
quickSort(arr, 0, arr.length - 1);
}
private static void quickSort(int[] arr, int low, int high) {
if (low < high) {
// 分区操作
int pi = partition(arr, low, high);
// 递归排序分区
quickSort(arr, low, pi - 1);
quickSort(arr, pi + 1, high);
}
}
private static int partition(int[] arr, int low, int high) {
// 选择基准 (这里选择最后一个元素)
int pivot = arr[high];
int i = low - 1; // 较小元素的索引
for (int j = low; j < high; j++) {
// 当前元素小于等于基准
if (arr[j] <= pivot) {
i++;
// 交换arr[i]和arr[j]
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
}
// 将基准元素放到正确位置
int temp = arr[i + 1];
arr[i + 1] = arr[high];
arr[high] = temp;
return i + 1;
}
// 7. 堆排序 - O(n log n)
public static void heapSort(int[] arr) {
int n = arr.length;
// 构建最大堆 (从最后一个非叶子节点开始)
for (int i = n / 2 - 1; i >= 0; i--) {
heapify(arr, n, i);
}
// 逐个提取堆顶元素
for (int i = n - 1; i > 0; i--) {
// 将当前堆顶移到数组末尾
int temp = arr[0];
arr[0] = arr[i];
arr[i] = temp;
// 对剩余元素重新堆化
heapify(arr, i, 0);
}
}
private static void heapify(int[] arr, int n, int i) {
int largest = i; // 初始化最大值为根节点
int left = 2 * i + 1; // 左子节点
int right = 2 * i + 2; // 右子节点
// 如果左子节点大于根节点
if (left < n && arr[left] > arr[largest]) {
largest = left;
}
// 如果右子节点大于当前最大值
if (right < n && arr[right] > arr[largest]) {
largest = right;
}
// 如果最大值不是根节点
if (largest != i) {
int swap = arr[i];
arr[i] = arr[largest];
arr[largest] = swap;
// 递归堆化受影响的子树
heapify(arr, n, largest);
}
}
// 8. 计数排序 - O(n + k) (k为数据范围)
public static void countingSort(int[] arr) {
if (arr.length == 0) return;
// 找出数组中的最大值和最小值
int max = Arrays.stream(arr).max().getAsInt();
int min = Arrays.stream(arr).min().getAsInt();
int range = max - min + 1;
int[] count = new int[range];
int[] output = new int[arr.length];
// 计数每个元素出现的次数
for (int value : arr) {
count[value - min]++;
}
// 计算累计计数
for (int i = 1; i < range; i++) {
count[i] += count[i - 1];
}
// 构建输出数组
for (int i = arr.length - 1; i >= 0; i--) {
output[count[arr[i] - min] - 1] = arr[i];
count[arr[i] - min]--;
}
// 复制回原数组
System.arraycopy(output, 0, arr, 0, arr.length);
}
// 9. 桶排序 - O(n + k) (k为桶的数量)
public static void bucketSort(int[] arr) {
if (arr.length == 0) return;
// 找出最大值和最小值
int max = Arrays.stream(arr).max().getAsInt();
int min = Arrays.stream(arr).min().getAsInt();
// 计算桶的数量 (这里使用平方根法)
int bucketCount = (int) Math.sqrt(arr.length);
int bucketSize = (max - min) / bucketCount + 1;
// 创建桶
int[][] buckets = new int[bucketCount][0];
// 分配元素到桶中
for (int value : arr) {
int bucketIndex = (value - min) / bucketSize;
buckets[bucketIndex] = append(buckets[bucketIndex], value);
}
// 对每个桶排序并合并
int index = 0;
for (int[] bucket : buckets) {
Arrays.sort(bucket);
for (int value : bucket) {
arr[index++] = value;
}
}
}
// 辅助方法:向数组添加元素
private static int[] append(int[] array, int value) {
int[] newArray = Arrays.copyOf(array, array.length + 1);
newArray[newArray.length - 1] = value;
return newArray;
}
// 10. 基数排序 - O(nk) (k为最大数字位数)
public static void radixSort(int[] arr) {
if (arr.length == 0) return;
// 找出最大值以确定最大位数
int max = Arrays.stream(arr).max().getAsInt();
// 对每个位数进行计数排序
for (int exp = 1; max / exp > 0; exp *= 10) {
countSortByDigit(arr, exp);
}
}
private static void countSortByDigit(int[] arr, int exp) {
int n = arr.length;
int[] output = new int[n];
int[] count = new int[10];
Arrays.fill(count, 0);
// 计算每个数字的出现次数
for (int value : arr) {
count[(value / exp) % 10]++;
}
// 计算累计计数
for (int i = 1; i < 10; i++) {
count[i] += count[i - 1];
}
// 构建输出数组
for (int i = n - 1; i >= 0; i--) {
output[count[(arr[i] / exp) % 10] - 1] = arr[i];
count[(arr[i] / exp) % 10]--;
}
// 复制回原数组
System.arraycopy(output, 0, arr, 0, n);
}
}
排序算法比较表
排序算法 | 时间复杂度(平均) | 时间复杂度(最坏) | 空间复杂度 | 稳定性 | 适用场景 |
---|---|---|---|---|---|
冒泡排序 | O(n²) | O(n²) | O(1) | 稳定 | 教学、小数据集 |
选择排序 | O(n²) | O(n²) | O(1) | 不稳定 | 教学、小数据集 |
插入排序 | O(n²) | O(n²) | O(1) | 稳定 | 小数据集、基本有序数据 |
希尔排序 | O(n log n) | O(n²) | O(1) | 不稳定 | 中等规模数据集 |
归并排序 | O(n log n) | O(n log n) | O(n) | 稳定 | 大数据集、链表排序 |
快速排序 | O(n log n) | O(n²) | O(log n) | 不稳定 | 通用排序、大规模数据集 |
堆排序 | O(n log n) | O(n log n) | O(1) | 不稳定 | 内存受限环境、实时系统 |
计数排序 | O(n + k) | O(n + k) | O(k) | 稳定 | 整数排序、值范围小 |
桶排序 | O(n + k) | O(n²) | O(n + k) | 稳定 | 均匀分布数据、外部排序 |
基数排序 | O(nk) | O(nk) | O(n + k) | 稳定 | 整数排序、字符串排序 |
TimSort | O(n log n) | O(n log n) | O(n) | 稳定 | Java/Python内置排序 |
算法选择建议
-
小数据集 (n < 100)
- 插入排序(性能好,代码简单)
- 选择排序(交换次数少)
-
中等数据集 (100 < n < 10,000)
- 快速排序(平均性能好)
- 希尔排序(不需要递归)
-
大数据集 (n > 10,000)
- 归并排序(稳定且性能保证)
- 堆排序(空间复杂度O(1))
- Java内置的
Arrays.sort()
(使用优化的TimSort)
-
特殊场景
- 整数排序且范围小 → 计数排序
- 均匀分布数据 → 桶排序
- 多关键字排序 → 基数排序
- 内存受限 → 堆排序
最佳实践
-
Java内置排序
Arrays.sort(arr); // 对基本类型使用双轴快速排序 Collections.sort(list); // 对对象使用TimSort
-
稳定性要求
- 需要稳定排序时选择:归并、插入、计数、桶排序或基数排序
-
空间考虑
- 内存受限:堆排序、希尔排序
- 空间充足:归并排序、桶排序
-
数据特性利用
- 基本有序:插入排序
- 大量重复元素:三路快速排序
-
并行优化
Arrays.parallelSort(arr); // Java 8+ 并行排序
此实现包含了10种常见排序算法,并提供了统一的测试框架。在实际应用中,建议优先使用Java内置的Arrays.sort()
方法,它针对不同情况进行了高度优化(对小数组使用插入排序,对大数组使用TimSort或双轴快速排序)。