Java 常见八大排序算法详解
·
1. 简单排序(基础、效率低,适合少量数据)
- 冒泡排序 BubbleSort
- 选择排序 SelectSort
- 插入排序 InsertSort
2. 进阶高效排序(工程常用)
- 希尔排序 ShellSort(插入优化)
- 快速排序 QuickSort(实际开发最常用)
- 归并排序 MergeSort(稳定,适合大数据 / 外部排序)
3. 堆排序 HeapSort(基于堆结构)
- 堆排序 HeapSort
4. 非比较排序(速度极快,有数值范围限制)
- 计数排序 CountSort
前置统一工具方法
所有示例共用打印数组方法,简化代码:
java
运行
public class SortDemo {
// 打印数组
public static void printArr(int[] arr) {
for (int num : arr) {
System.out.print(num + " ");
}
System.out.println();
}
public static void main(String[] args) {
int[] arr = {5, 3, 8, 4, 2, 7, 1, 6};
// 此处替换对应排序方法
}
}
一、冒泡排序 Bubble Sort
通俗理解
像水里气泡往上浮:相邻两个数字两两对比,左边 > 右边就交换,一轮结束最大值沉到末尾;多轮循环,每轮少比较末尾已排好的数字。
特点
- 稳定排序(相等数字相对位置不变)
- 时间复杂度:最坏 O (n²),最好 O (n)(优化后有序数组一次遍历)
- 适合:数据量很小的场景
java
运行
// 冒泡排序(带优化:有序直接退出)
public static void bubbleSort(int[] arr) {
int len = arr.length;
// 外层控制轮数,一共 len-1 轮
for (int i = 0; i < len - 1; i++) {
boolean isSorted = true; // 标记本轮是否发生交换
// 每轮比较到 len-1-i,后面i个已经排好
for (int j = 0; j < len - 1 - i; j++) {
// 左边比右边大,交换
if (arr[j] > arr[j + 1]) {
int temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
isSorted = false;
}
}
// 本轮无交换,说明数组完全有序,直接结束
if (isSorted) break;
}
}
调用测试:
java
运行
int[] arr = {5, 3, 8, 4, 2, 7, 1, 6};
bubbleSort(arr);
printArr(arr); // 输出:1 2 3 4 5 6 7 8
二、选择排序 Select Sort
通俗理解
遍历数组找到最小值下标,和当前最左边未排序位置交换;一轮确定一个最小值放前面。
特点
- 不稳定排序
- 时间复杂度固定 O (n²),无论有序与否都要完整遍历
- 交换次数比冒泡少,但整体依然很慢
java
运行
public static void selectSort(int[] arr) {
int len = arr.length;
// i:当前待填充的最小值位置
for (int i = 0; i < len - 1; i++) {
int minIndex = i; // 先假设当前i是最小值下标
// 从i+1往后找真正最小值
for (int j = i + 1; j < len; j++) {
if (arr[j] < arr[minIndex]) {
minIndex = j;
}
}
// 交换最小值到i位置
if (minIndex != i) {
int temp = arr[i];
arr[i] = arr[minIndex];
arr[minIndex] = temp;
}
}
}
三、插入排序 Insert Sort
通俗理解
打扑克牌:左手是有序牌,右手拿一张新牌,从后往前对比,插入到合适位置。 数组分为:左边有序区、右边无序区,逐个把无序数字插入有序区。
特点
- 稳定排序
- 数组越接近有序,速度越快;几乎有序数据效率极高
- 时间复杂度最坏 O (n²),最好 O (n)
java
运行
public static void insertSort(int[] arr) {
int len = arr.length;
// 从第二个元素开始(第一个默认有序)
for (int i = 1; i < len; i++) {
int temp = arr[i]; // 待插入数字
int j;
// 从i-1向前遍历有序区,比temp大就后移
for (j = i - 1; j >= 0 && arr[j] > temp; j--) {
arr[j + 1] = arr[j];
}
// 腾出的空位放入temp
arr[j + 1] = temp;
}
}
四、希尔排序 Shell Sort(插入排序优化版)
通俗理解
插入排序每次只相邻对比,移动成本高;希尔先按间隔 gap分组组内插入排序,逐步缩小 gap 直到 gap=1(普通插入)。 先远距离粗调,再精细微调,大幅减少元素移动次数。
特点
- 不稳定排序
- 效率远高于普通插入,中等数据量表现不错
java
运行
public static void shellSort(int[] arr) {
int len = arr.length;
// 初始间隔为长度一半,每次减半
for (int gap = len / 2; gap > 0; gap /= 2) {
// 组内插入排序,逻辑和插入排序一致,步长gap
for (int i = gap; i < len; i++) {
int temp = arr[i];
int j;
for (j = i - gap; j >= 0 && arr[j] > temp; j -= gap) {
arr[j + gap] = arr[j];
}
arr[j + gap] = temp;
}
}
}
五、快速排序 Quick Sort(工程最常用)
通俗理解
分治思想:选一个基准值 pivot,把数组分成两部分:左边全部 ≤ pivot,右边全部 ≥ pivot;再递归排序左右两部分。
特点
- 不稳定排序
- 平均时间复杂度 O (n log n),绝大多数场景最快
- 最坏 O (n²)(完全有序数组,可通过随机基准优化)
- JDK Arrays.sort () 基础类型底层就是双轴快排
java
运行
// 快排入口
public static void quickSort(int[] arr) {
quick(arr, 0, arr.length - 1);
}
// 递归处理 [left, right] 区间
private static void quick(int[] arr, int left, int right) {
if (left >= right) return; // 区间只有一个数,无需排序
int l = left;
int r = right;
int pivot = arr[l]; // 选最左为基准
while (l < r) {
// 右指针向左找小于pivot的数
while (l < r && arr[r] >= pivot) r--;
arr[l] = arr[r]; // 填到左指针空位
// 左指针向右找大于pivot的数
while (l < r && arr[l] <= pivot) l++;
arr[r] = arr[l]; // 填到右指针空位
}
arr[l] = pivot; // 基准归位,l是分割点
// 递归左区间
quick(arr, left, l - 1);
// 递归右区间
quick(arr, l + 1, right);
}
六、归并排序 Merge Sort
通俗理解
分治:先把数组不断对半拆分,拆到单个元素天然有序;再两两合并有序数组,一层一层合并直到完整数组。
特点
- 稳定排序(唯一稳定的 O (n log n) 排序)
- 时间复杂度稳定 O (n log n),无最坏情况
- 需要额外辅助数组,占用内存大;适合海量文件外部排序
java
运行
// 归排入口
public static void mergeSort(int[] arr) {
int[] temp = new int[arr.length]; // 辅助数组,避免反复创建
merge(arr, 0, arr.length - 1, temp);
}
// 拆分区间
private static void merge(int[] arr, int left, int right, int[] temp) {
if (left >= right) return;
int mid = (left + right) / 2;
merge(arr, left, mid, temp); // 左半拆分
merge(arr, mid + 1, right, temp); // 右半拆分
mergeTwo(arr, left, mid, right, temp); // 合并两个有序区间
}
// 合并 [left,mid] 和 [mid+1,right] 两个有序数组
private static void mergeTwo(int[] arr, int left, int mid, int right, int[] temp) {
int i = left; // 左数组起点
int j = mid + 1;// 右数组起点
int t = 0; // temp数组指针
// 两边同时遍历,取较小值放入temp
while (i <= mid && j <= right) {
if (arr[i] <= arr[j]) {
temp[t++] = arr[i++];
} else {
temp[t++] = arr[j++];
}
}
// 剩余左边元素放入temp
while (i <= mid) temp[t++] = arr[i++];
// 剩余右边元素放入temp
while (j <= right) temp[t++] = arr[j++];
// 将temp有序数据拷贝回原数组
t = 0;
while (left <= right) arr[left++] = temp[t++];
}
七、堆排序 Heap Sort
通俗理解
基于大顶堆(父节点 ≥ 左右子节点):
- 把数组构建成大顶堆,堆顶是最大值;
- 堆顶和末尾元素交换,最大值固定到数组尾部;
- 剩余未排序部分重新调整为大顶堆,重复操作。
特点
- 不稳定排序
- 时间复杂度稳定 O (n log n),内存占用比归并小
java
运行
public static void heapSort(int[] arr) {
int len = arr.length;
// 1. 构建大顶堆,从最后一个非叶子节点倒序遍历
for (int i = len / 2 - 1; i >= 0; i--) {
adjustHeap(arr, i, len);
}
// 2. 堆顶和末尾交换,逐步固定最大值
for (int i = len - 1; i > 0; i--) {
// 交换堆顶最大值和末尾
int temp = arr[0];
arr[0] = arr[i];
arr[i] = temp;
// 剩余数组重新调整堆
adjustHeap(arr, 0, i);
}
}
// 调整以i为根的堆,len是当前堆有效长度
private static void adjustHeap(int[] arr, int i, int len) {
int parent = arr[i]; // 父节点值
// 左子节点下标
for (int left = 2 * i + 1; left < len; left = 2 * left + 1) {
// 右子节点存在且更大,切换到右子节点
if (left + 1 < len && arr[left] < arr[left + 1]) {
left++;
}
// 子节点大于父节点,交换
if (arr[left] > parent) {
arr[i] = arr[left];
i = left;
} else {
break;
}
}
arr[i] = parent; // 父节点放到最终正确位置
}
八、计数排序 Count Sort(非比较排序)
通俗理解
不比较大小,利用数字本身做下标:
- 找到数组最大值,创建计数数组;
- 统计每个数字出现次数;
- 按计数顺序回填原数组。
特点
- 速度极快 O (n+k),k 是数值范围
- 只能排整数,数值跨度极大时浪费内存
java
运行
public static void countSort(int[] arr) {
if (arr.length == 0) return;
// 找到最大值确定计数数组长度
int max = arr[0];
for (int num : arr) {
if (num > max) max = num;
}
int[] countArr = new int[max + 1];
// 统计每个数字出现次数
for (int num : arr) {
countArr[num]++;
}
// 按顺序回填原数组
int index = 0;
for (int i = 0; i < countArr.length; i++) {
while (countArr[i] > 0) {
arr[index++] = i;
countArr[i]--;
}
}
}
九、排序算法核心对比总结
表格
| 排序 | 平均复杂度 | 稳定性 | 额外空间 | 适用场景 |
|---|---|---|---|---|
| 冒泡 | O(n²) | 稳定 | O(1) | 极小数据、教学 |
| 选择 | O(n²) | 不稳定 | O(1) | 几乎不用 |
| 插入 | O(n²) | 稳定 | O(1) | 接近有序小数组 |
| 希尔 | O(n^1.3) | 不稳定 | O(1) | 中等少量数据 |
| 快速 | O(n logn) | 不稳定 | O(logn) | 日常开发首选 |
| 归并 | O(n logn) | 稳定 | O(n) | 需要稳定排序、外部文件排序 |
| 堆排序 | O(n logn) | 不稳定 | O(1) | 内存有限、不要求稳定 |
| 计数排序 | O(n+k) | 稳定 | O(k) | 整数、数值范围小 |
补充开发实用提示
- Java 内置排序:
Arrays.sort()- byte/short/int/long 基础类型:双轴快速排序
- 对象引用类型:TimSort(归并 + 插入结合,稳定)
- 千万级大数据优先归并;内存紧张选堆排;常规业务数组直接用快排。
更多推荐


所有评论(0)