8 大经典排序算法(Java 实现):原理 + Demo + 核心分析

本文完整梳理冒泡、插入、选择、快速、归并、计数、桶、基数8 种排序算法的核心逻辑,并提供可直接运行的 Java 代码示例,同时标注每种算法的关键特性(时间复杂度、稳定性、适用场景),帮你一站式掌握排序算法的落地实现。

先明确核心评价维度

表格

指标 说明
时间复杂度 算法执行时间与数据量 n 的关系(最好 / 最坏 / 平均)
空间复杂度 额外占用的内存空间(O (1) 为「原地排序」,最优)
稳定性 排序后,值相等的元素相对位置不变(如 [2,2,1] 排序后,两个 2 的位置不颠倒)

1. 冒泡排序(Bubble Sort)

核心原理

相邻元素两两比较,逆序则交换,每轮将最大元素 "冒泡" 到末尾;可优化:若某轮无交换,说明已排序,直接终止。

关键特性

  • 时间复杂度:最好 O (n)、最坏 O (n²)、平均 O (n²)
  • 空间复杂度:O (1)(原地排序)
  • 稳定性:稳定

Java Demo

java

运行

复制代码
public class BubbleSort {
    public static void bubbleSort(int[] arr) {
        if (arr == null || arr.length <= 1) return;
        int n = arr.length;
        // 标记是否发生交换,优化版本
        boolean swapped;
        for (int i = 0; i < n - 1; i++) {
            swapped = false;
            // 每轮结束后,最后i个元素已排序,无需再比较
            for (int j = 0; j < n - 1 - i; 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;
        }
    }

    public static void main(String[] args) {
        int[] arr = {3, 1, 4, 1, 5, 9, 2, 6};
        bubbleSort(arr);
        System.out.println("冒泡排序结果:" + Arrays.toString(arr));
        // 输出:[1, 1, 2, 3, 4, 5, 6, 9]
    }
}

2. 选择排序(Selection Sort)

核心原理

将数组分为 "已排序区" 和 "未排序区",每轮找到未排序区的最小值,放到已排序区末尾。

关键特性

  • 时间复杂度:最好 / 最坏 / 平均都是 O (n²)
  • 空间复杂度:O (1)(原地排序)
  • 稳定性:不稳定(交换会破坏相等元素相对位置)

Java Demo

java

运行

复制代码
public class SelectionSort {
    public static void selectionSort(int[] arr) {
        if (arr == null || arr.length <= 1) return;
        int n = arr.length;
        for (int i = 0; i < n - 1; i++) {
            // 记录最小值的索引(关键:先找索引,最后再交换)
            int minIndex = i;
            for (int j = i + 1; j < n; j++) {
                if (arr[j] < arr[minIndex]) {
                    minIndex = j;
                }
            }
            // 交换当前位置和最小值位置的元素
            if (minIndex != i) {
                int temp = arr[i];
                arr[i] = arr[minIndex];
                arr[minIndex] = temp;
            }
        }
    }

    public static void main(String[] args) {
        int[] arr = {3, 1, 4, 1, 5, 9, 2, 6};
        selectionSort(arr);
        System.out.println("选择排序结果:" + Arrays.toString(arr));
        // 输出:[1, 1, 2, 3, 4, 5, 6, 9]
    }
}

3. 插入排序(Insertion Sort)

核心原理

类似整理扑克牌,将数组分为 "已排序区"(初始为第一个元素)和 "未排序区",逐个将未排序元素插入到已排序区的正确位置。

关键特性

  • 时间复杂度:最好 O (n)、最坏 O (n²)、平均 O (n²)
  • 空间复杂度:O (1)(原地排序)
  • 稳定性:稳定

Java Demo

java

运行

复制代码
public class InsertionSort {
    public static void insertionSort(int[] arr) {
        if (arr == null || arr.length <= 1) return;
        int n = arr.length;
        for (int i = 1; i < n; i++) {
            // 待插入的元素
            int temp = arr[i];
            // 已排序区的末尾指针
            int j = i - 1;
            // 找到插入位置(已排序区元素大于temp则后移)
            while (j >= 0 && arr[j] > temp) {
                arr[j + 1] = arr[j];
                j--;
            }
            // 插入元素
            arr[j + 1] = temp;
        }
    }

    public static void main(String[] args) {
        int[] arr = {3, 1, 4, 1, 5, 9, 2, 6};
        insertionSort(arr);
        System.out.println("插入排序结果:" + Arrays.toString(arr));
        // 输出:[1, 1, 2, 3, 4, 5, 6, 9]
    }
}

4. 快速排序(Quick Sort)

核心原理

分治思想:选 "基准值"(pivot),将数组分为 "小于基准""等于基准""大于基准" 三部分,递归排序左右两部分;优化:随机选基准避免最坏情况。

关键特性

  • 时间复杂度:最好 O (nlogn)、最坏 O (n²)、平均 O (nlogn)
  • 空间复杂度:O (logn)(递归栈空间)
  • 稳定性:不稳定

Java Demo

java

运行

复制代码
import java.util.Arrays;
import java.util.Random;

public class QuickSort {
    private static final Random random = new Random();

    public static void quickSort(int[] arr) {
        if (arr == null || arr.length <= 1) return;
        quickSort(arr, 0, arr.length - 1);
    }

    private static void quickSort(int[] arr, int left, int right) {
        if (left >= right) return;
        // 随机选基准,避免有序数组导致的最坏情况
        int pivotIndex = left + random.nextInt(right - left + 1);
        // 将基准交换到左边界
        swap(arr, left, pivotIndex);
        int pivot = arr[left];
        
        // 分区:[left+1, lt] < pivot,[lt+1, i) = pivot,[gt, right] > pivot
        int lt = left;   // 小于区的右边界
        int gt = right + 1; // 大于区的左边界
        int i = left + 1;   // 当前遍历指针
        
        while (i < gt) {
            if (arr[i] < pivot) {
                lt++;
                swap(arr, i, lt);
                i++;
            } else if (arr[i] > pivot) {
                gt--;
                swap(arr, i, gt);
            } else {
                i++;
            }
        }
        // 将基准放到小于区和等于区的中间
        swap(arr, left, lt);
        
        // 递归排序左右部分
        quickSort(arr, left, lt - 1);
        quickSort(arr, gt, right);
    }

    private static void swap(int[] arr, int i, int j) {
        int temp = arr[i];
        arr[i] = arr[j];
        arr[j] = temp;
    }

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

5. 归并排序(Merge Sort)

核心原理

分治思想:将数组递归拆分为两半,分别排序后,再合并两个有序子数组;核心是 "合并" 操作。

关键特性

  • 时间复杂度:最好 / 最坏 / 平均都是 O (nlogn)
  • 空间复杂度:O (n)(需要临时数组存储合并结果)
  • 稳定性:稳定

Java Demo

java

运行

复制代码
import java.util.Arrays;

public class MergeSort {
    public static void mergeSort(int[] arr) {
        if (arr == null || arr.length <= 1) return;
        // 临时数组,避免递归中重复创建
        int[] temp = new int[arr.length];
        mergeSort(arr, 0, arr.length - 1, temp);
    }

    private static void mergeSort(int[] arr, int left, int right, int[] temp) {
        if (left >= right) return;
        // 分:拆分为左右两半
        int mid = left + (right - left) / 2;
        mergeSort(arr, left, mid, temp);
        mergeSort(arr, mid + 1, right, temp);
        // 治:合并两个有序子数组
        merge(arr, left, mid, right, temp);
    }

    private static void merge(int[] arr, int left, int mid, int right, int[] temp) {
        // 复制数据到临时数组
        System.arraycopy(arr, left, temp, left, right - left + 1);
        
        int i = left;     // 左子数组指针
        int j = mid + 1;  // 右子数组指针
        int k = left;     // 原数组指针
        
        // 合并两个有序子数组
        while (i <= mid && j <= right) {
            if (temp[i] <= temp[j]) {
                arr[k++] = temp[i++];
            } else {
                arr[k++] = temp[j++];
            }
        }
        // 复制左子数组剩余元素
        while (i <= mid) {
            arr[k++] = temp[i++];
        }
        // 右子数组剩余元素无需复制(已在原数组中)
    }

    public static void main(String[] args) {
        int[] arr = {3, 1, 4, 1, 5, 9, 2, 6};
        mergeSort(arr);
        System.out.println("归并排序结果:" + Arrays.toString(arr));
        // 输出:[1, 1, 2, 3, 4, 5, 6, 9]
    }
}

6. 计数排序(Counting Sort)

核心原理

非比较排序:统计每个数值出现的次数,再根据次数重构有序数组;仅适用于数值范围小且为整数的场景。

关键特性

  • 时间复杂度:O (n + k)(k 为数值范围)
  • 空间复杂度:O (k)(计数数组空间)
  • 稳定性:稳定(可优化实现)

Java Demo

java

运行

复制代码
import java.util.Arrays;

public class CountingSort {
    public static void countingSort(int[] arr) {
        if (arr == null || arr.length <= 1) return;
        // 步骤1:找到数组的最大值和最小值
        int min = arr[0], max = arr[0];
        for (int num : arr) {
            if (num < min) min = num;
            if (num > max) max = num;
        }
        // 步骤2:创建计数数组,统计每个数值的出现次数
        int[] count = new int[max - min + 1];
        for (int num : arr) {
            count[num - min]++;
        }
        // 步骤3:重构有序数组(稳定版:累加计数数组)
        // 累加计数数组,得到每个数值的最后位置
        for (int i = 1; i < count.length; i++) {
            count[i] += count[i - 1];
        }
        // 倒序遍历原数组,保证稳定性
        int[] temp = new int[arr.length];
        for (int i = arr.length - 1; i >= 0; i--) {
            int index = count[arr[i] - min] - 1;
            temp[index] = arr[i];
            count[arr[i] - min]--;
        }
        // 复制回原数组
        System.arraycopy(temp, 0, arr, 0, arr.length);
    }

    public static void main(String[] args) {
        int[] arr = {3, 1, 4, 1, 5, 9, 2, 6};
        countingSort(arr);
        System.out.println("计数排序结果:" + Arrays.toString(arr));
        // 输出:[1, 1, 2, 3, 4, 5, 6, 9]
    }
}

7. 桶排序(Bucket Sort)

核心原理

将数据分配到多个 "桶" 中,每个桶内单独排序(可用插入排序 / 快速排序),最后合并所有桶;适用于数据分布均匀的场景。

关键特性

  • 时间复杂度:最好 O (n + k)、最坏 O (n²)、平均 O (n + k)(k 为桶数)
  • 空间复杂度:O (n + k)
  • 稳定性:稳定(取决于桶内排序算法)

Java Demo

java

运行

复制代码
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;

public class BucketSort {
    public static void bucketSort(int[] arr) {
        if (arr == null || arr.length <= 1) return;
        // 步骤1:确定桶的数量和范围
        int min = arr[0], max = arr[0];
        for (int num : arr) {
            if (num < min) min = num;
            if (num > max) max = num;
        }
        // 桶的数量:根据数据分布调整,这里取10个桶
        int bucketNum = 10;
        List<List<Integer>> buckets = new ArrayList<>(bucketNum);
        // 初始化桶
        for (int i = 0; i < bucketNum; i++) {
            buckets.add(new ArrayList<>());
        }
        // 步骤2:将数据分配到对应桶中
        for (int num : arr) {
            // 计算数据所属桶的索引
            int bucketIndex = (num - min) * (bucketNum - 1) / (max - min);
            buckets.get(bucketIndex).add(num);
        }
        // 步骤3:每个桶内排序,再合并
        int index = 0;
        for (List<Integer> bucket : buckets) {
            // 桶内用插入排序/Collections.sort(底层是归并排序)
            Collections.sort(bucket);
            for (int num : bucket) {
                arr[index++] = num;
            }
        }
    }

    public static void main(String[] args) {
        int[] arr = {3, 1, 4, 1, 5, 9, 2, 6};
        bucketSort(arr);
        System.out.println("桶排序结果:" + Arrays.toString(arr));
        // 输出:[1, 1, 2, 3, 4, 5, 6, 9]
    }
}

8. 基数排序(Radix Sort)

核心原理

非比较排序:按数字的 "位"(个位、十位、百位...)依次排序,每轮用计数排序 / 桶排序保证稳定性;适用于整数 / 字符串排序。

关键特性

  • 时间复杂度:O (d*(n + k))(d 为位数,k 为基数,如十进制 k=10)
  • 空间复杂度:O (n + k)
  • 稳定性:稳定

Java Demo

java

运行

复制代码
import java.util.Arrays;

public class RadixSort {
    public static void radixSort(int[] arr) {
        if (arr == null || arr.length <= 1) return;
        // 步骤1:找到最大值,确定最大位数
        int max = arr[0];
        for (int num : arr) {
            if (num > max) max = num;
        }
        // 步骤2:按位排序(个位、十位、百位...)
        for (int exp = 1; max / exp > 0; exp *= 10) {
            countingSortByDigit(arr, exp);
        }
    }

    // 按指定位(exp=1:个位,exp=10:十位)进行计数排序
    private static void countingSortByDigit(int[] arr, int exp) {
        int n = arr.length;
        int[] output = new int[n];
        int[] count = new int[10]; // 十进制,0-9
        
        // 统计当前位的数字出现次数
        for (int num : arr) {
            int digit = (num / exp) % 10;
            count[digit]++;
        }
        // 累加计数数组,确定位置
        for (int i = 1; i < 10; i++) {
            count[i] += count[i - 1];
        }
        // 倒序遍历,保证稳定性
        for (int i = n - 1; i >= 0; i--) {
            int digit = (arr[i] / exp) % 10;
            output[count[digit] - 1] = arr[i];
            count[digit]--;
        }
        // 复制回原数组
        System.arraycopy(output, 0, arr, 0, n);
    }

    public static void main(String[] args) {
        int[] arr = {3, 1, 4, 1, 5, 9, 2, 6};
        radixSort(arr);
        System.out.println("基数排序结果:" + Arrays.toString(arr));
        // 输出:[1, 1, 2, 3, 4, 5, 6, 9]
    }
}

核心总结(8 种排序对比)

表格

排序算法 时间复杂度 空间复杂度 稳定性 适用场景
冒泡 O(n²) O(1) 稳定 小数据量、几乎有序的数据
选择 O(n²) O(1) 不稳定 小数据量、交换成本高的场景
插入 O(n²) O(1) 稳定 小数据量、几乎有序的数据
快速 O(nlogn) O(logn) 不稳定 通用场景、内存排序首选
归并 O(nlogn) O(n) 稳定 需稳定排序、外排序(磁盘)
计数 O(n+k) O(k) 稳定 数值范围小的整数排序
O(n+k) O(n+k) 稳定 数据分布均匀的场景
基数 O(d*(n+k)) O(n+k) 稳定 整数 / 字符串、位数固定的场景

关键选型建议

  1. 通用场景优先选快速排序(性能最优);
  2. 需稳定排序选归并排序
  3. 数值范围小选计数排序 ,数据分布均匀选桶排序 ,整数 / 字符串选基数排序
  4. 小数据量(n<1000)选插入排序(实际性能优于快速排序)。
相关推荐
智能工业品检测-奇妙智能1 小时前
AIFlowy如何实现与现有Spring Boot项目的无缝集成?
java·spring boot·后端
We་ct1 小时前
LeetCode 77. 组合:DFS回溯+剪枝,高效求解组合问题
开发语言·前端·算法·leetcode·typescript·深度优先·剪枝
從南走到北1 小时前
JAVA无人共享无人健身房物联网结合系统源码支持小程序+公众号+APP+H5
java·物联网·小程序
Nuopiane1 小时前
MyPal3(3)
java·开发语言
重生之我是Java开发战士1 小时前
【递归、搜索与回溯】二叉树中的深度优先搜索:布尔二叉树,求根节点到叶节点数字之和,二叉树剪枝,验证二叉搜索树,第K小的元素,二叉树的所有路径
算法·深度优先·剪枝
篮l球场1 小时前
矩阵置零
算法
lihihi1 小时前
P1650 [ICPC 2004 Shanghai R] 田忌赛马(同洛谷2587)
开发语言·算法·r语言
朱一头zcy1 小时前
[牛客]BC38 变种水仙花
算法
努力学算法的蒟蒻2 小时前
day105(3.6)——leetcode面试经典150
算法·leetcode·面试