算法之常见八大排序

发布于:2025-09-09 ⋅ 阅读:(24) ⋅ 点赞:(0)

算法说明:

  1. 冒泡排序:重复比较相邻元素,将较大的元素向后移动
  2. 选择排序:每次选择最小元素放到已排序序列末尾
  3. 插入排序:将元素插入到已排序序列的正确位置
  4. 希尔排序:改进的插入排序,通过分组减少移动次数
  5. 归并排序:分治法,递归排序子数组后合并
  6. 快速排序:选择枢轴元素,将数组分为两部分递归排序
  7. 堆排序:利用堆数据结构进行选择排序
  8. 桶排序:将元素分配到多个桶中,分别排序后合并

代码案例:

import java.util.Arrays;

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::bucketSort, arr.clone());
    }
    
    private static void testSort(String name, RunnableSorter sorter, int[] arr) {
        long startTime = System.nanoTime();
        sorter.run(arr);
        long endTime = System.nanoTime();
        System.out.printf("%s: %s (耗时: %.3f ms)%n", 
                          name, 
                          Arrays.toString(arr),
                          (endTime - startTime) / 1_000_000.0);
    }
    
    @FunctionalInterface
    interface RunnableSorter {
        void run(int[] arr);
    }
    
	// 八大排序实现方法
    // 1. 冒泡排序:重复比较相邻元素,将较大的元素向后移动
    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. 选择排序:每次选择最小元素放到已排序序列末尾
    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. 插入排序:将元素插入到已排序序列的正确位置
    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. 希尔排序:改进的插入排序,通过分组减少移动次数
    public static 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;
            }
        }
    }

    // 5. 归并排序:分治法,递归排序子数组后合并
    public static void mergeSort(int[] arr) {
        if (arr.length > 1) {
            int mid = arr.length / 2;
            int[] left = Arrays.copyOfRange(arr, 0, mid);
            int[] right = Arrays.copyOfRange(arr, mid, arr.length);
            
            mergeSort(left);
            mergeSort(right);
            
            // 合并两个有序数组
            int i = 0, j = 0, k = 0;
            while (i < left.length && j < right.length) {
                if (left[i] < right[j]) {
                    arr[k++] = left[i++];
                } else {
                    arr[k++] = right[j++];
                }
            }
            // 处理剩余元素
            while (i < left.length) arr[k++] = left[i++];
            while (j < right.length) arr[k++] = right[j++];
        }
    }

    // 6. 快速排序:选择枢轴元素,将数组分为两部分递归排序
    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++;
                // 交换元素
                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. 堆排序:利用堆数据结构进行选择排序
    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. 桶排序:将元素分配到多个桶中,分别排序后合并
    // 桶排序 (假设元素范围在0-100)
    public static void bucketSort(int[] arr) {
        if (arr.length == 0) return;
        
        // 确定最大值
        int maxVal = Arrays.stream(arr).max().getAsInt();
        
        // 创建桶
        int bucketCount = maxVal / 10 + 1;
        int[][] buckets = new int[bucketCount][0];
        
        // 分配元素到桶中
        for (int num : arr) {
            int bucketIndex = num / 10;
            buckets[bucketIndex] = Arrays.copyOf(buckets[bucketIndex], buckets[bucketIndex].length + 1);
            buckets[bucketIndex][buckets[bucketIndex].length - 1] = num;
        }
        
        // 对每个桶排序(这里使用插入排序)
        int index = 0;
        for (int[] bucket : buckets) {
            if (bucket.length > 0) {
                insertionSort(bucket); // 可以使用任意排序算法
                for (int num : bucket) {
                    arr[index++] = num;
                }
            }
        }
    }
}

注意:桶排序的性能取决于数据分布和桶的数量,代码中假设数据范围在0-100,实际使用时需要根据数据范围调整桶的数量。

时间复杂度对比:

算法 平均时间复杂度 最坏时间复杂度 空间复杂度 稳定性
冒泡 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²) O(n+k) 稳定

网站公告

今日签到

点亮在社区的每一天
去签到