跳到主要内容Java 经典排序算法全解析 | 极客日志Javajava算法
Java 经典排序算法全解析
Java 排序算法涵盖插入、希尔、选择、堆、冒泡、快速、归及非比较排序。文章详解各算法原理、代码实现及性能对比,包含三数取中、非递归快排等优化方案,适用于不同数据规模与稳定性需求场景。
蜜桃汽水1 浏览 排序基础
在深入具体算法之前,先明确几个核心概念。稳定的排序是指排序前后相同元素的相对顺序不变;内部排序指数据完全在内存中完成;外部排序则涉及磁盘等外部存储,通常用于海量数据。
插入排序
核心思路是假设第一个元素已有序,从第二个元素开始,将其与前面已排序的元素比较,找到合适位置插入。当待排序数组基本有序时,效率极高。
public static void insertSort(int[] array) {
for (int i = 1; i < array.length; i++) {
int j = i - 1;
int tmp = array[i];
for (; j >= 0; j--) {
if (array[j] > tmp) {
array[j + 1] = array[j];
} else {
break;
}
}
array[j + 1] = tmp;
}
}
性能分析:
- 时间复杂度:O(N^2),最好情况(已有序)为 O(N)。
- 空间复杂度:O(1)。
- 稳定性:稳定。
希尔排序
这是对直接插入排序的优化。通过分组缩小增量,让较大的元素快速后移,较小的元素快速前移。最后将整体视为一组进行插入,此时序列已基本有序,效率接近 O(N)。
public static void shellSort(int[] array) {
int gap = array.length;
while (gap > 1) {
gap /= 2;
shell(array, gap);
}
}
private static {
( gap; i < array.length; i++) {
i - gap;
array[i];
(; j >= ; j -= gap) {
(array[j] > tmp) {
array[j + gap] = array[j];
} {
;
}
}
array[j + gap] = tmp;
}
}
void
shell
(int[] array, int gap)
for
int
i
=
int
j
=
int
tmp
=
for
0
if
else
break
- 时间复杂度:O(N^1.3 ~ N^1.5),取决于增量序列。
- 空间复杂度:O(1)。
- 稳定性:不稳定。
选择排序
在当前下标 i 之后寻找最小值,并与 i 位置的元素交换。存在一种双向优化的版本,同时寻找最大和最小值分别交换到两端,但需注意索引更新逻辑,防止最大值被提前交换覆盖。
public static void selectSort(int[] array) {
int n = array.length;
for (int i = 0; i < n; i++) {
int minIndex = i;
for (int j = i + 1; j < n; j++) {
if (array[j] < array[minIndex]) {
minIndex = j;
}
}
swap(i, minIndex, array);
}
}
private static void swap(int i, int j, int[] array) {
int tmp = array[i];
array[i] = array[j];
array[j] = tmp;
}
- 时间复杂度:O(N^2)。
- 空间复杂度:O(1)。
- 稳定性:不稳定。
堆排序
利用大根堆的性质,每次将堆顶元素(最大值)与末尾元素交换,然后调整剩余堆结构。适合对空间要求严格且需要部分有序的场景。
public static void heapSort(int[] array) {
createHeap(array);
int end = array.length - 1;
while (end > 0) {
swap(end, 0, array);
shiftDown(array, 0, end);
end--;
}
}
private static void shiftDown(int[] array, int parent, int len) {
int child = 2 * parent + 1;
while (child < len) {
if (child + 1 < len && array[child] < array[child + 1]) {
child++;
}
if (array[child] > array[parent]) {
swap(child, parent, array);
parent = child;
child = 2 * parent + 1;
} else {
break;
}
}
}
private static void createHeap(int[] array) {
for (int parent = (array.length - 1 - 1) / 2; parent >= 0; parent--) {
shiftDown(array, parent, array.length);
}
}
- 时间复杂度:O(N logN)。
- 空间复杂度:O(1)。
- 稳定性:不稳定。
冒泡排序
相邻元素两两比较,大的往后沉。加入标志位 flag 可在数组已有序时提前结束,避免无效遍历。
public static void bubbleSort(int[] array) {
for (int i = 0; i < array.length; i++) {
boolean flag = true;
for (int j = 0; j < array.length - i - 1; j++) {
if (array[j] > array[j + 1]) {
swap(j, j + 1, array);
flag = false;
}
}
if (flag) {
break;
}
}
}
- 时间复杂度:O(N^2)。
- 空间复杂度:O(1)。
- 稳定性:稳定。
快速排序
快速排序是实际应用中最高效的排序算法之一,核心是分治思想。有多种分区实现方式,推荐优先使用挖坑法或 Hoare 法。
分区策略
- Hoare 法:左右指针向中间靠拢,相遇处即为基准位置。注意先从右指针开始找小值,避免死循环。
- 挖坑法:基准值先取出,右边找小填左边坑,左边找大填右边坑,最后基准归位。
- 前后指针法:维护
prev 和 cur,cur 找小于基准的值交给 prev,最后交换基准。
public static void quickSort(int[] array) {
quick(array, 0, array.length - 1);
}
private static void quick(int[] array, int start, int end) {
if (start >= end) return;
int pivot = partitionHoare(array, start, end);
quick(array, start, pivot - 1);
quick(array, pivot + 1, end);
}
private static int partitionHoare(int[] array, int left, int right) {
int tmp = array[left];
int i = left;
while (left < right) {
while (left < right && array[right] >= tmp) right--;
while (left < right && array[left] <= tmp) left++;
swap(left, right, array);
}
swap(i, left, array);
return left;
}
优化技巧
- 三数取中:选取首、尾、中间三个数的中位数作为基准,避免极端情况下退化为 O(N^2)。
- 小区间插入排序:当递归区间较小时(如长度小于 15),切换为插入排序,减少递归开销。
- 非递归实现:使用栈模拟递归过程,避免栈溢出风险。
private static int middleNum(int[] array, int left, int right) {
int mid = (left + right) / 2;
if (array[left] < array[right]) {
if (array[mid] < array[left]) return left;
else if (array[mid] > array[right]) return right;
else return mid;
} else {
if (array[mid] > array[left]) return left;
else if (array[right] > array[mid]) return right;
else return mid;
}
}
private static void quickOptimized(int[] array, int start, int end) {
if (start >= end) return;
if (end - start + 1 <= 15) {
insertSort(array, start, end);
return;
}
int index = middleNum(array, start, end);
swap(index, start, array);
int pivot = partitionHoare(array, start, end);
quickOptimized(array, start, pivot - 1);
quickOptimized(array, pivot + 1, end);
}
- 时间复杂度:平均 O(N logN),最坏 O(N^2)。
- 空间复杂度:平均 O(logN),最坏 O(N)。
- 稳定性:不稳定。
归并排序
典型的分治算法,将数组分解为两个有序子段,再合并为一个有序数组。由于需要额外空间,空间复杂度较高,但稳定性好。
public static void mergeSort(int[] array) {
merge(array, 0, array.length - 1);
}
private static void merge(int[] array, int start, int end) {
if (start >= end) return;
int mid = (start + end) / 2;
merge(array, start, mid);
merge(array, mid + 1, end);
mergeHeB(array, start, mid, end);
}
private static void mergeHeB(int[] array, int left, int mid, int right) {
int s1 = left, e1 = mid;
int s2 = mid + 1, e2 = right;
int k = 0;
int[] tmpArray = new int[right - left + 1];
while (s1 <= e1 && s2 <= e2) {
if (array[s1] < array[s2]) tmpArray[k++] = array[s1++];
else tmpArray[k++] = array[s2++];
}
while (s1 <= e1) tmpArray[k++] = array[s1++];
while (s2 <= e2) tmpArray[k++] = array[s2++];
for (int i = 0; i < tmpArray.length; i++) {
array[i + left] = tmpArray[i];
}
}
非递归实现:通过控制步长 gap,自底向上合并,避免递归调用。
- 时间复杂度:O(N logN)。
- 空间复杂度:O(N)。
- 稳定性:稳定。
非比较排序
计数排序
统计每个数字出现的次数,按顺序写回原数组。适用于数据范围有限的场景。
public static void countSort(int[] array) {
int minVal = array[0], maxVal = array[0];
for (int x : array) {
if (x < minVal) minVal = x;
if (x > maxVal) maxVal = x;
}
int len = maxVal - minVal + 1;
int[] count = new int[len];
for (int x : array) count[x - minVal]++;
int k = 0;
for (int i = 0; i < count.length; i++) {
while (count[i] > 0) {
array[k++] = i + minVal;
count[i]--;
}
}
}
基数排序与桶排序
基数排序按位分配收集,桶排序将数据分到不同区间桶内排序。两者均依赖数据分布特性。
- 小规模或基本有序:插入排序。
- 通用高效:快速排序(配合优化)。
- 稳定性要求高:归并排序。
- 空间敏感:堆排序。
- 数据范围有限:计数/基数排序。
相关免费在线工具
- Keycode 信息
查找任何按下的键的javascript键代码、代码、位置和修饰符。 在线工具,Keycode 信息在线工具,online
- Escape 与 Native 编解码
JavaScript 字符串转义/反转义;Java 风格 \uXXXX(Native2Ascii)编码与解码。 在线工具,Escape 与 Native 编解码在线工具,online
- JavaScript / HTML 格式化
使用 Prettier 在浏览器内格式化 JavaScript 或 HTML 片段。 在线工具,JavaScript / HTML 格式化在线工具,online
- JavaScript 压缩与混淆
Terser 压缩、变量名混淆,或 javascript-obfuscator 高强度混淆(体积会增大)。 在线工具,JavaScript 压缩与混淆在线工具,online
- 加密/解密文本
使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online
- Gemini 图片去水印
基于开源反向 Alpha 混合算法去除 Gemini/Nano Banana 图片水印,支持批量处理与下载。 在线工具,Gemini 图片去水印在线工具,online