Java 中常见排序算法的完整实现

以下是 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)稳定整数排序、字符串排序
TimSortO(n log n)O(n log n)O(n)稳定Java/Python内置排序

算法选择建议

  1. 小数据集 (n < 100)

    • 插入排序(性能好,代码简单)
    • 选择排序(交换次数少)
  2. 中等数据集 (100 < n < 10,000)

    • 快速排序(平均性能好)
    • 希尔排序(不需要递归)
  3. 大数据集 (n > 10,000)

    • 归并排序(稳定且性能保证)
    • 堆排序(空间复杂度O(1))
    • Java内置的Arrays.sort()(使用优化的TimSort)
  4. 特殊场景

    • 整数排序且范围小 → 计数排序
    • 均匀分布数据 → 桶排序
    • 多关键字排序 → 基数排序
    • 内存受限 → 堆排序

最佳实践

  1. Java内置排序

    Arrays.sort(arr); // 对基本类型使用双轴快速排序
    Collections.sort(list); // 对对象使用TimSort
    
  2. 稳定性要求

    • 需要稳定排序时选择:归并、插入、计数、桶排序或基数排序
  3. 空间考虑

    • 内存受限:堆排序、希尔排序
    • 空间充足:归并排序、桶排序
  4. 数据特性利用

    • 基本有序:插入排序
    • 大量重复元素:三路快速排序
  5. 并行优化

    Arrays.parallelSort(arr); // Java 8+ 并行排序
    

此实现包含了10种常见排序算法,并提供了统一的测试框架。在实际应用中,建议优先使用Java内置的Arrays.sort()方法,它针对不同情况进行了高度优化(对小数组使用插入排序,对大数组使用TimSort或双轴快速排序)。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值