文章中如果有图看不到,可以点这里去 csdn 看看。从那边导过来的,文章太多,没法一篇篇修改好。

八大经典排序算法的 Java 实现&对比(冒泡、选择、插入、希尔、归并、快排、计数、堆排序)【面试必考】

以下是八大经典排序算法的Java实现,包含时间复杂度、空间复杂度分析、特点说明及优化策略:


1. 冒泡排序 (Bubble Sort)

public 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]) {
                swap(arr, j, j + 1);
                swapped = true;
            }
        }
        if (!swapped) break; // 无交换说明已有序
    }
}
  • 时间复杂度
    • 最好:O(n)(已有序)
    • 最坏:O(n²)
    • 平均:O(n²)
  • 空间复杂度:O(1)(原地排序)
  • 特点
    • 稳定排序
    • 简单但效率低
  • 优化
    • 添加 swapped 标志提前终止

2. 选择排序 (Selection Sort)

public 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;
        }
        swap(arr, i, minIdx);
    }
}
  • 时间复杂度:O(n²)(始终需遍历剩余元素)
  • 空间复杂度:O(1)
  • 特点
    • 不稳定(如 [5, 5, 2]
    • 交换次数少(最多 n-1 次)

3. 插入排序 (Insertion Sort)

public void insertionSort(int[] arr) {
    int n = arr.length;
    for (int i = 1; i < n; i++) {
        int key = arr[i];
        int j = i - 1;
        while (j >= 0 && arr[j] > key) {
            arr[j + 1] = arr[j];
            j--;
        }
        arr[j + 1] = key;
    }
}
  • 时间复杂度
    • 最好:O(n)(已有序)
    • 最坏:O(n²)
  • 空间复杂度:O(1)
  • 特点
    • 稳定排序
    • 对小规模/基本有序数据高效
  • 优化
    • 二分插入排序(减少比较次数)

4. 希尔排序 (Shell Sort)

public void shellSort(int[] arr) {
    int n = arr.length;
    for (int gap = n / 2; gap > 0; gap /= 2) {
        for (int i = gap; i < n; i++) {
            int temp = arr[i];
            int j;
            for (j = i; j >= gap && arr[j - gap] > temp; j -= gap) {
                arr[j] = arr[j - gap];
            }
            arr[j] = temp;
        }
    }
}
  • 时间复杂度
    • 平均:O(n log n) ~ O(n²)(取决于步长序列)
    • 最优:O(n log² n)(Knuth序列)
  • 空间复杂度:O(1)
  • 特点
    • 不稳定(跨步长交换)
    • 插入排序的改进版

5. 归并排序 (Merge Sort)

public 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 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) {
        temp[k++] = arr[i] <= arr[j] ? arr[i++] : arr[j++];
    }
    while (i <= mid) temp[k++] = arr[i++];
    while (j <= right) temp[k++] = arr[j++];
    System.arraycopy(temp, 0, arr, left, temp.length);
}
  • 时间复杂度:O(n log n)(始终)
  • 空间复杂度:O(n)(需额外数组)
  • 特点
    • 稳定排序
    • 适合链表/外部排序
  • 优化
    • 小规模子数组用插入排序

6. 快速排序 (Quick Sort)

public void quickSort(int[] arr, int low, int high) {
    if (low < high) {
        int pivot = partition(arr, low, high);
        quickSort(arr, low, pivot - 1);
        quickSort(arr, pivot + 1, high);
    }
}

private 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++;
            swap(arr, i, j);
        }
    }
    swap(arr, i + 1, high);
    return i + 1;
}
  • 时间复杂度
    • 最好:O(n log n)
    • 最坏:O(n²)(数组已有序)
  • 空间复杂度:O(log n)(递归栈)
  • 特点
    • 不稳定(如 [3, 2, 2]
    • 平均性能最快
  • 优化
    • 三数取中法选基准
    • 小数组转插入排序
    • 尾递归优化

7. 堆排序 (Heap Sort)

public 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--) {
        swap(arr, 0, i);
        heapify(arr, i, 0);
    }
}

private 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) {
        swap(arr, i, largest);
        heapify(arr, n, largest);
    }
}
  • 时间复杂度:O(n log n)(建堆 O(n),调整 O(log n))
  • 空间复杂度:O(1)
  • 特点
    • 不稳定(堆调整可能破坏顺序)
    • 适合取 Top K 问题

8. 计数排序 (Counting Sort)

public void countingSort(int[] arr) {
    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 num : arr) count[num - 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);
}
  • 时间复杂度:O(n + k)(k 为数据范围)
  • 空间复杂度:O(n + k)
  • 特点
    • 稳定排序(反向填充)
    • 仅适用于整数且范围小的情况

总结对比表

排序算法最好时间复杂度最坏时间复杂度平均时间复杂度空间复杂度稳定性
冒泡排序O(n)O(n²)O(n²)O(1)稳定
选择排序O(n²)O(n²)O(n²)O(1)不稳定
插入排序O(n)O(n²)O(n²)O(1)稳定
希尔排序O(n log n)O(n²)O(n^{1.3})O(1)不稳定
归并排序O(n log n)O(n log n)O(n log n)O(n)稳定
快速排序O(n log n)O(n²)O(n log n)O(log n)不稳定
堆排序O(n log n)O(n log n)O(n log n)O(1)不稳定
计数排序O(n + k)O(n + k)O(n + k)O(n + k)稳定

使用场景建议

  • 小规模数据:插入排序(简单且稳定)
  • 通用高效排序:快速排序(平均性能最优)
  • 需要稳定性:归并排序(稳定且 O(n log n))
  • 数据范围有限:计数排序(如年龄排序)
  • 外部排序:归并排序(处理大文件)
  • Top K 问题:堆排序(无需全排序)

实际开发中优先使用标准库(如 Arrays.sort()),其内部根据数据特征自动选择最优算法(如插入+归并+快排组合)。

posted @ 2025-09-13 11:54  NeoLshu  阅读(7)  评论(0)    收藏  举报  来源