冒泡、快排、堆排的实现逻辑与选择思路

冒泡排序是最直观的排序方法。从头开始两两比较相邻元素,前面比后面大就交换,一趟下来最大的元素沉到了最后。重复n-1趟就排完了。加一个swapped标志可以提前结束,某一趟没有发生交换就说明已经有序了。

复制代码
public static void bubbleSort(int[] arr) {
int n = arr.length;
for (int i = 0; i < n - 1; i++) {
boolean swapped = false;
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;
}
}

冒泡排序是稳定的,相等的元素不会交换。时间复杂度O(n²),适合数据量小的场景。

快速排序是分治思想的典型应用。每次选一个基准值,通过一趟扫描把比基准小的放左边,比基准大的放右边,基准就放到了它最终的位置上。然后递归处理左右两部分。

分区的过程:用i和j两个指针,j从左到右扫描,遇到比基准小的就和i位置交换,i后移。扫描完后i左边的都比基准小,i右边都比基准大,最后把基准放到i的位置。

复制代码
public static void quickSort(int[] arr, int low, int high) {
if (low < high) {
int pi = partition(arr, low, high);
quickSort(arr, low, pi - 1);
quickSort(arr, pi + 1, high);
}
}

private static int partition(int[] arr, int low, int high) {
int pivot = arr[high];
int i = low - 1;
for (int j = low; j < high; j++) {
if (arr[j] <= pivot) {
i++;
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
}
int temp = arr[i + 1];
arr[i + 1] = arr[high];
arr[high] = temp;
return i + 1;
}

快速排序平均O(n log n),但极端情况下会退化成O(n²),比如数组已经有序且基准总选最后一个。实际工程里会对基准选择做优化,比如三数取中。

堆排序利用了大顶堆的性质:父节点永远比子节点大。第一步把无序数组构建成大顶堆,从最后一个非叶子节点开始依次下沉。第二步反复把堆顶和末尾交换,然后调整剩余元素重新成堆。

建堆的复杂度是O(n),每次调整是O(log n),总复杂度O(n log n)。堆排序不稳定,因为跳跃式交换会打乱相等元素的相对顺序。

复制代码
public static void heapSort(int[] arr) {
int n = arr.length;
for (int i = n / 2 - 1; i >= 0; i--) {
heapify(arr, n, i);
}
for (int i = n - 1; i > 0; i--) {
int temp = arr[0];
arr[0] = arr[i];
arr[i] = temp;
heapify(arr, i, 0);
}
}

private static void heapify(int[] arr, int n, int i) {
int largest = i;
int left = 2 * i + 1;
int right = 2 * i + 2;
if (left < n && arr[left] > arr[largest]) largest = left;
if (right < n && arr[right] > arr[largest]) largest = right;
if (largest != i) {
int swap = arr[i];
arr[i] = arr[largest];
arr[largest] = swap;
heapify(arr, n, largest);
}
}

三种排序怎么选:数据量小、要求稳定用冒泡。数据量大、随机性强用快排,它常数小,实际跑起来最快。不想让快排出现最坏情况,或者对空间有要求,堆排序更稳健。归并排序虽然也是O(n log n)且稳定,但要额外开空间,这里没展开。

相关推荐
旋律4149 分钟前
String、StringBuilder、StringBuffer:三个"字符串",到底区别在哪?
java·后端
lengxuenong15 分钟前
树状数组 · 信息学学习手册2-离散化与逆序对
c++·学习·算法
琳达爱分享16 分钟前
五个线程同时去换 token,四个被踢下线:刷新接口其实不是幂等的
后端·算法·架构
十二17 分钟前
【洛谷题解】P1115 最大子段和(前缀和 + 维护最小前缀)
数据结构·c++·算法·蓝桥杯
一条泥憨鱼1 小时前
【Redis | 黑马点评】基于Redis的消息队列 | stream消息队列 源码解读
java·数据库·redis·缓存·lua
aibo8603081 小时前
基于 Spring Boot + Vue3 的博物馆藏品管理系统设计与实现
java·spring boot·后端
fanruitian1 小时前
python 进程 线程
java·前端·python
renhongxia11 小时前
RAG检索增强生成:打通大模型与外部知识的通道
大数据·人工智能·算法
\光辉岁月/1 小时前
javaSE8-新特性-Stream&Option
java·stream·option
zzu123zsw1 小时前
把条件校验收敛到枚举接口:Spring Boot 统一断言模式与零污染异常切面工程化
java·spring boot·后端·架构设计