几种排序-Java

6 阅读4分钟
  1. 插入排序

插入排序的核心思想是:将数组分为已排序和未排序两部分,依次将未排序部分的元素插入到已排序部分的合适位置。

    public static void main(String[] args) {
        int[] arr = {101, 34, 119, 1};
        insertionSort(arr); // 调用插入排序
        System.out.println(java.util.Arrays.toString(arr)); // 输出排序结果
    }

    // 插入排序核心方法
    public static void insertionSort(int[] arr) {
        // 从第2个元素开始(索引1),默认第一个元素已排序
        for (int i = 1; i < arr.length; i++) {
            int key = arr[i]; // 暂存当前待插入元素
            int j = i - 1; // 指向已排序区间最后一个元素

            // 向前遍历已排序区间,找到插入位置(元素大于key则后移)
            while (j >= 0 && arr[j] > key) {
                arr[j + 1] = arr[j]; // 元素后移,腾出插入位置
                j--;
            }
            // 若位置变化,将key插入对应位置
            if (j + 1 != i) {
                arr[j + 1] = key;
            }
        }
    }
}
  1. 希尔排序

希尔排序是插入排序的改进版:先将数组按「步长」分组,对每组进行插入排序;逐步缩小步长,最终步长为 1 时,数组已基本有序,再做一次插入排序。

    public static void main(String[] args) {
        int[] arr = {8, 9, 1, 7, 2, 3, 5, 4, 6, 0};
        shellSort(arr);
        System.out.println(java.util.Arrays.toString(arr));
    }

    public static void shellSort(int[] arr) {
        for (int gap = arr.length / 2; gap > 0; gap /= 2) {
            for (int i = gap; i < arr.length; i++) {
                int temp = arr[i];
                int j = i - gap;
                while (j >= 0 && temp < arr[j]) {
                    arr[j + gap] = arr[j];
                    j -= gap;
                }
                if (j + gap != i) arr[j + gap] = temp;
            }
        }
    }
}
  1. 快速排序

快速排序是分治思想:选择一个「基准值」,将数组分为 “小于基准值” 和 “大于基准值” 两部分,再递归处理子数组。

    public static void main(String[] args) {
        int[] arr = {5, 2, 9, 3, 8, 4, 1, 7, 6};
        quickSort(arr); // 调用快速排序
        for (int num : arr) { // 输出排序结果
            System.out.print(num + " ");
        }
    }

    // 快速排序入口(封装)
    public static void quickSort(int[] arr) {
        if (arr == null || arr.length < 2) return; // 空/长度<2无需排序
        quickSort2(arr, 0, arr.length - 1); // 递归处理整个数组
    }

    // 递归分区排序
    public static void quickSort2(int[] arr, int left, int right) {
        if (left >= right) return; // 递归终止:区间只有1个元素
        int pivotIndex = partition(arr, left, right); // 分区,返回基准值索引
        quickSort2(arr, left, pivotIndex - 1); // 处理基准值左区间
        quickSort2(arr, pivotIndex + 1, right); // 处理基准值右区间
    }

    // 分区核心:以右边界为基准值,划分<=基准值和>基准值区间
    public static int partition(int[] arr, int left, int right) {
        int pivot = arr[right]; // 基准值(右边界元素)
        int index = left; // 记录<=基准值区间的右边界

        // 遍历区间,将<=基准值的元素移到左侧
        for (int i = left; i < right; i++) {
            if (arr[i] <= pivot) {
                swap(arr, index++, i); // 交换元素,扩大<=基准值区间
            }
        }
        swap(arr, index, right); // 将基准值放到最终位置
        return index; // 返回基准值索引
    }

    // 交换数组两个元素
    public static void swap(int[] arr, int a, int b) {
        int temp = arr[a];
        arr[a] = arr[b];
        arr[b] = temp;
    }
}
  1. 选择排序

选择排序的核心:每一轮从未排序区间找到最小元素,与未排序区间的第一个元素交换,逐步扩大已排序区间。

    public static void main(String[] args) {
        int[] arr = {101, 34, 119, 1};
        selectionSort(arr); // 调用选择排序
        System.out.println(java.util.Arrays.toString(arr)); // 输出排序结果
    }

    // 选择排序核心方法
    public static void selectionSort(int[] arr) {
        // 外层循环:控制未排序区间起始位置
        for (int i = 0; i < arr.length - 1; i++) {
            int minIndex = i; // 初始化最小值索引为未排序区间第一个元素

            // 内层循环:找未排序区间的最小值索引
            for (int j = i + 1; j < arr.length; j++) {
                if (arr[j] < arr[minIndex]) minIndex = j;
            }
            // 最小值不在起始位置则交换
            if (i != minIndex) {
                int temp = arr[i];
                arr[i] = arr[minIndex];
                arr[minIndex] = temp;
            }
        }
    }
}
  1. 归并排序

归并排序基于「分治思想」:先将数组递归拆分为最小单元,再将拆分后的有序子数组合并为一个有序数组。

    public static void main(String[] args) {
        int[] arr = {8, 4, 5, 7, 1, 4, 6, 2};
        mergeSort(arr, 0, arr.length - 1); // 调用归并排序(左0,右最后一位)
        System.out.println(java.util.Arrays.toString(arr)); // 输出排序结果
    }

    // 递归拆分数组
    public static void mergeSort(int[] arr, int left, int right) {
        if (left >= right) return; // 递归终止:区间只有1个元素
        int mid = (left + right) / 2; // 计算中间索引,拆分左右区间
        mergeSort(arr, left, mid); // 递归处理左区间
        mergeSort(arr, mid + 1, right); // 递归处理右区间
        merge(arr, left, mid, right); // 合并两个有序子区间
    }

    // 合并两个有序子数组
    public 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; // i=左区间指针,j=右区间指针,k=临时数组指针

        // 比较左右区间元素,将较小值放入临时数组
        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++];
        // 临时数组结果复制回原数组
        for (k = 0; k < temp.length; k++) {
            arr[left + k] = temp[k];
        }
    }
}