Skip to content

排序算法

冒泡排序

算法介绍

冒泡排序(Bubble Sort),它重复地遍历要排序的列表,比较相邻的元素并根据需要交换它们的位置。这个过程会将每次遍历中最大(或最小)的未排序元素“冒泡”到列表的末尾。遍历列表的工作重复进行直到没有需要交换的元素为止,这意味着列表已经排序完成。

算法逻辑

  1. 从数组的第一个元素开始,依次比较相邻的两个元素。
  2. 如果前一个元素大于后一个元素,则交换它们的位置(升序排序时)。
  3. 每次遍历会将当前未排序部分中最大的元素“冒泡”到正确的位置(即末尾)。
  4. 重复上述过程,直到整个数组有序。

文字示例(升序)

原始数组:[5, 3, 8, 4, 2]

  1. 第一轮遍历:
    1. 比较 5 和 3 → 交换 → [3, 5, 8, 4, 2]
    2. 比较 5 和 8 → 不交换
    3. 比较 8 和 4 → 交换 → [3, 5, 4, 8, 2]
    4. 比较 8 和 2 → 交换 → [3, 5, 4, 2, 8](最大值已到位)
  2. 第二轮遍历(忽略最后一位8):
    1. 比较 3 和 5 → 不交换
    2. 比较 5 和 4 → 交换 → [3, 4, 5, 2, 8]
    3. 比较 5 和 2 → 交换 → [3, 4, 2, 5, 8](次大值到位)
    4. 继续进行下去,直到全部有序。

代码示例

冒泡排序 升序
java
import java.util.Scanner;

public class BubbleSortDemo {

    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        System.out.println("请输入数组长度:");
        int arrLength = scanner.nextInt();

        int[] arr = new int[arrLength];

        for(int i = 0; i < arrLength; i++) {
            int temp = i + 1;
            System.out.println("请输入第" + temp + "个元素值:");
            arr[i] = scanner.nextInt();
        }

        bubbleSort(arr);

        System.out.println("排序后的数组为:");
        for (int i = 0; i < arrLength; i++) {
            System.out.print(arr[i] + " ");
        }

    }

    public static void bubbleSort(int[] arr) {
        boolean swap;
        for (int i = 0; i < arr.length - 1; i++) {
            swap = false;
            for (int j = 0; j < arr.length - 1 - i; j++) {
                if (arr[j] > arr[j + 1]) {
                    int temp = arr[j];
                    arr[j] = arr[j + 1];
                    arr[j + 1] = temp;
                    swap = true;
                }
            }

            if(!swap) {
                break;
            }

        }
    }
}

选择排序

插入排序

快速排序

算法介绍

快速排序(Quick Sort)是一种基于分治策略的高效排序算法。它的核心思想是:通过一趟排序将待排序列分割成独立的两部分,其中一部分的所有元素均比基准值小,另一部分均比基准值大,然后递归地对这两部分继续进行排序,最终使整个序列有序。快速排序的平均时间复杂度为 O(n log n),在大多数情况下表现优异。

算法逻辑

从数组中选取一个基准元素(通常选择最后一个元素)。

重新排列数组,使得所有小于基准的元素位于基准左侧,所有大于基准的元素位于基准右侧(分区操作)。此时基准元素处于其最终正确位置。

递归地对基准左侧和右侧的子数组重复上述步骤,直到子数组的长度为 0 或 1(自然有序)。

文字示例(升序)

原始数组:[8, 4, 7, 2, 5, 1, 3, 6]

  1. 选择最后一个元素 6 作为基准。

  2. 第一次分区:

    • 初始化 i = -1(指向小于基准区域的最后一个位置)。

    • 依次遍历 j = 0 到 j = 6:

      • j=0,8 > 6,不交换。

      • j=1,4 ≤ 6,i 变为 0,交换 arr[0] 和 arr[1] → [4, 8, 7, 2, 5, 1, 3, 6]。

      • j=2,7 > 6,不交换。

      • j=3,2 ≤ 6,i 变为 1,交换 arr[1] 和 arr[3] → [4, 2, 7, 8, 5, 1, 3, 6]。

      • j=4,5 ≤ 6,i 变为 2,交换 arr[2] 和 arr[4] → [4, 2, 5, 8, 7, 1, 3, 6]。

      • j=5,1 ≤ 6,i 变为 3,交换 arr[3] 和 arr[5] → [4, 2, 5, 1, 7, 8, 3, 6]。

      • j=6,3 ≤ 6,i 变为 4,交换 arr[4] 和 arr[6] → [4, 2, 5, 1, 3, 8, 7, 6]。

      • 最后将基准 6 与 arr[i+1](即 arr[5])交换 → [4, 2, 5, 1, 3, 6, 7, 8]。

      • 此时基准 6 已归位,索引为 5,左子数组为 [4, 2, 5, 1, 3](均小于 6),右子数组为 [7, 8](均大于 6)。

  3. 处理左子数组 [4, 2, 5, 1, 3](基准为最后一个元素 3):

    • 初始化 i = -1,遍历 j = 0 到 j = 3:

      • j=0,4 > 3,不交换。

      • j=1,2 ≤ 3,i 变为 0,交换 arr[0] 和 arr[1] → [2, 4, 5, 1, 3]。

      • j=2,5 > 3,不交换。

      • j=3,1 ≤ 3,i 变为 1,交换 arr[1] 和 arr[3] → [2, 1, 5, 4, 3]。

    • 将基准 3 与 arr[i+1](arr[2])交换 → [2, 1, 3, 4, 5]。

    • 基准 3 归位,索引 2,左子数组 [2, 1],右子数组 [4, 5]。

  4. 处理左子数组 [2, 1](基准为最后一个元素 1):

    • 初始化 i = -1,遍历 j = 0:

      • j=0,2 > 1,不交换。
    • 将基准 1 与 arr[i+1](arr[0])交换 → [1, 2]。

    • 基准 1 归位,索引 0,左子数组为空,右子数组 [2](自然有序)。

  5. 处理右子数组 [4, 5](基准为最后一个元素 5):

    • 初始化 i = -1,遍历 j = 0:

      • j=0,4 ≤ 5,i 变为 0,交换 arr[0] 与自身(不变)→ [4, 5]。
    • 将基准 5 与 arr[i+1](arr[1])交换,数组仍为 [4, 5]。

    • 基准 5 归位,索引 1,左子数组 [4],右子数组为空。

  6. 处理右子数组 [7, 8](基准为最后一个元素 8):

    • 初始化 i = -1,遍历 j = 0:

      • j=0,7 ≤ 8,i 变为 0,交换 arr[0] 与自身(不变)→ [7, 8]。
    • 将基准 8 与 arr[i+1](arr[1])交换,数组仍为 [7, 8]。

    • 基准 8 归位,索引 1,左子数组 [7],右子数组为空。

  7. 最终合并结果:所有子数组已有序,组合得到完整有序数组 [1, 2, 3, 4, 5, 6, 7, 8]。

代码示例

java
/**
 * 快速排序
 */
public class QuickSort {

    public static void main(String[] args) {
        int[] arr = {10, 7, 8, 9, 1, 5};
        quickSort(arr, 0, arr.length - 1);
        System.out.println("排序后:" + java.util.Arrays.toString(arr));
    }

    /**
     * 交换索引下标为 i 和 索引下标为 j 的数组的值
     * @param array 数组值
     * @param i 索引 i
     * @param j 索引 j
     */
    public static void swap(int[] array, int i, int j) {
        int temp = array[i];
        array[i] = array[j];
        array[j] = temp;
    }

    /**
     * 分区
     * @param array 数组
     * @param low 开始索引下标
     * @param high 结束索引下标
     * @return 基准索引下标
     */
    public static int partition(int[] array, int low, int high) {
        // 选择最后一个元素作为基准
        int pivot = array[high];

        // i 指向小于基准的区域的最后一个位置,初始为 low - 1
        int i = low - 1;

        for (int j = low; j < high; j++) {
            if (array[j] <= pivot) {
                swap(array, ++i, j);
            }
        }

        swap(array, ++i, high);

        // 返回基准的最终索引
        return i;
    }

    // 快速排序入口
    public static void quickSort(int[] array, int low, int high) {
        if (low < high) {
            int pivotIndex = partition(array, low, high);
            quickSort(array, low, pivotIndex - 1);
            quickSort(array, pivotIndex + 1, high);
        }
    }
}

归并排序