说到排序算法,是不是总让你觉得头大?别慌!这篇文章通过专业版+人话版的“双语”说明,手把手带你轻松搞懂十大排序算法( ^ ^ )/■
一、冒泡排序(Bubble Sort)
专业版
原理:通过相邻元素比较交换,使较大元素逐步"浮动"到数组末端
时间复杂度:
• 最优:O(n)(已有序时)
• 平均:O(n²)
• 最差:O(n²)
空间复杂度:O(1)
稳定性:稳定
人话版
像煮水时气泡上浮,每一轮把最大的数推到数组末尾,虽然简单但效率较低
public static void bubbleSort(int[] arr) {
for (int i = 0; i < arr.length-1; i++) {
for (int j = 0; j < arr.length-i-1; j++) {
if (arr[j] > arr[j+1]) {
int temp = arr[j];
arr[j] = arr[j+1];
arr[j+1] = temp;
}
}
}
}
🔗 相关题目:75. 颜色分类
二、选择排序(Selection Sort)
专业版
原理:每次遍历选择最小元素放入已排序序列末尾
时间复杂度:O(n²)(所有情况)
优势:数据交换次数最少(n-1次)
人话版
像在菜市场挑菜,每次找到最小的菜放到袋子最前面,直到全部挑完
public static void selectionSort(int[] arr) {
for (int i = 0; i < arr.length-1; i++) {
int minIndex = i;
for (int j = i+1; j < arr.length; j++) {
if (arr[j] < arr[minIndex]) minIndex = j;
}
int temp = arr[i];
arr[i] = arr[minIndex];
arr[minIndex] = temp;
}
}
🔗 相关题目:215. 数组第K大元素(选择变种)
三、插入排序(Insertion Sort)
专业版
原理:构建有序序列,逐个插入未排序元素到正确位置
最佳场景:数据基本有序时接近O(n)
人话版
像整理扑克牌,每次拿到新牌都插入到手牌的正确位置。
public static void insertionSort(int[] arr) {
for (int i = 1; i < arr.length; i++) {
int key = arr[i], j = i-1;
while (j >= 0 && arr[j] > key) {
arr[j+1] = arr[j];
j--;
}
arr[j+1] = key;
}
}
🔗 相关题目:147. 链表插入排序
四、希尔排序(Shell Sort)
专业版
原理:改进的插入排序,通过动态间隔分组实现高效移动
时间复杂度:O(n log n) ~ O(n²)
人话版
像用不同筛孔的筛子分组筛沙,先粗筛后细筛,逐步精细化排序。
public static void shellSort(int[] arr) {
for (int gap = arr.length/2; gap > 0; gap /= 2) {
for (int i = gap; i < arr.length; i++) {
int j = i, temp = arr[j];
while (j >= gap && arr[j-gap] > temp) {
arr[j] = arr[j-gap];
j -= gap;
}
arr[j] = temp;
}
}
}
🔗 相关题目:常用于嵌入式系统等内存受限场景
五、归并排序(Merge Sort)
专业版
原理:分治法 + 有序数组合并,稳定排序算法
空间复杂度:O(n)
人话版
像拼图游戏,先把大图拆成碎片,再两两合并成有序大块
public static void mergeSort(int[] arr, int left, int right) {
if (left >= right) return;
int mid = left + (right-left)/2;
mergeSort(arr, left, mid);
mergeSort(arr, mid+1, right);
merge(arr, left, mid, right);
}
private static void merge(int[] arr, int left, int mid, int right) {
int[] temp = new int[right-left+1];
int i = left, j = mid+1, k = 0;
while (i <= mid && j <= right)
temp[k++] = arr[i] <= arr[j] ? arr[i++] : arr[j++];
while (i <= mid) temp[k++] = arr[i++];
while (j <= right) temp[k++] = arr[j++];
System.arraycopy(temp, 0, arr, left, temp.length);
}
🔗 相关题目:148. 排序链表
六、快速排序(Quick Sort)
专业版
原理:分治策略 + 基准值分区,综合效率最高的排序算法
优化方案:三数取中法、尾递归优化
人话版
像切蛋糕,选个基准数把数组切成两半,左边小右边大,再递归处理
public static void quickSort(int[] arr, int left, int right) { if (left >= right) return; int pivot = partition(arr, left, right); quickSort(arr, left, pivot-1); quickSort(arr, pivot+1, right); } private static int partition(int[] arr, int left, int right) { int pivot = arr[left]; while (left < right) { while (left < right && arr[right] >= pivot) right--; arr[left] = arr[right]; while (left < right && arr[left] <= pivot) left++; arr[right] = arr[left]; } arr[left] = pivot; return left; }
🔗 相关题目:912. 排序数组
-
- *java
七、堆排序(Heap Sort)
专业版
原理:利用堆结构特性实现选择排序,原地排序算法
优势场景:动态Top K维护
人话版
像拆积木塔,每次拿走最顶端的积木(最大值),然后重建积木塔
public static void heapSort(int[] arr) {
for (int i = arr.length/2-1; i >= 0; i--)
heapify(arr, i, arr.length);
for (int j = arr.length-1; j > 0; j--) {
swap(arr, 0, j);
heapify(arr, 0, j);
}
}
private static void heapify(int[] arr, int i, int len) {
int largest = i, left = 2*i+1, right = 2*i+2;
if (left < len && arr[left] > arr[largest]) largest = left;
if (right < len && arr[right] > arr[largest]) largest = right;
if (largest != i) {
swap(arr, i, largest);
heapify(arr, largest, len);
}
}
🔗 相关题目:347. 前K高频元素
八、计数排序(Counting Sort)
专业版
原理:空间换时间,统计元素出现次数
限制:仅适用于整数且范围较小的情况
人话版
像投票统计,先数每个候选人得票数,再按票数顺序排列
public static void countingSort(int[] arr) {
int max = Arrays.stream(arr).max().getAsInt();
int min = Arrays.stream(arr).min().getAsInt();
int[] count = new int[max - min + 1];
for (int num : arr) count[num - min]++;
int index = 0;
for (int i = 0; i < count.length; i++) {
while (count[i]-- > 0) {
arr[index++] = i + min;
}
}
}
🔗 相关题目:274. H指数
九、桶排序(Bucket Sort)
专业版
原理:将数据分到有限数量的有序桶中,再分别排序
最佳场景:数据均匀分布时接近O(n)
人话版
像分快递,先把包裹按地区分到不同筐里,再分别给每个筐排序
public static List<Integer> bucketSort(List<Integer> arr, int bucketSize) {
if (arr.isEmpty()) return arr;
int max = Collections.max(arr);
int min = Collections.min(arr);
int bucketCount = (max - min)/bucketSize + 1;
List<List<Integer>> buckets = new ArrayList<>(bucketCount);
for (int i = 0; i < bucketCount; i++)
buckets.add(new ArrayList<>());
for (int num : arr)
buckets.get((num - min)/bucketSize).add(num);
List<Integer> result = new ArrayList<>();
for (List<Integer> bucket : buckets) {
Collections.sort(bucket);
result.addAll(bucket);
}
return result;
}
🔗 相关题目:164. 最大间距
十、基数排序(Radix Sort)
专业版
原理:按数字位数从低位到高位依次进行稳定排序
优势场景:电话号码、日期等位数固定数据
人话版
像整理扑克牌,先按花色分组,再按数字排序,逐位整理
public static void radixSort(int[] arr) {
int max = Arrays.stream(arr).max().getAsInt();
int maxDigit = (int) Math.log10(max) + 1;
for (int d = 0; d < maxDigit; d++) {
Queue<Integer>[] buckets = new LinkedList[10];
for (int i = 0; i < 10; i++)
buckets[i] = new LinkedList<>();
for (int num : arr) {
int digit = (num / (int)Math.pow(10, d)) % 10;
buckets[digit].offer(num);
}
int index = 0;
for (Queue<Integer> bucket : buckets) {
while (!bucket.isEmpty())
arr[index++] = bucket.poll();
}
}
}
🔗 相关题目:179. 最大数
综合对比
排序法 | 平均时间复杂度 | 最佳时间复杂度 | 最差时间复杂度 | 空间复杂度 | 稳定性 | 适用场景 |
---|---|---|---|---|---|---|
冒泡排序 | O(n²) | O(n) | O(n²) | O(1) | 稳定 | 教学演示/小数据校验 |
选择排序 | O(n²) | O(n²) | O(n²) | O(1) | 不稳定 | 简单场景/数据交换最少需求 |
插入排序 | O(n²) | O(n) | O(n²) | O(1) | 稳定 | 小规模数据/近似有序数据 |
希尔排序 | O(n log n)~O(n²) | O(n log n) | O(n²) | O(1) | 不稳定 | 中等规模数据/内存受限场景 |
快速排序 | O(n log n) | O(n log n) | O(n²) | O(log n) | 不稳定 | 通用数据排序(综合最优) |
归并排序 | O(n log n) | O(n log n) | O(n log n) | O(n) | 稳定 | 链表排序/外部排序/稳定排序需求 |
堆排序 | O(n log n) | O(n log n) | O(n log n) | O(1) | 不稳定 | 大规模数据/Top K问题 |
计数排序 | O(n + k) | O(n + k) | O(n + k) | O(k) | 稳定 | 小范围整数排序(k为数据范围) |
桶排序 | O(n + k) | O(n + k) | O(n²) | O(n + k) | 稳定 | 均匀分布数据/外部排序 |
基数排序 | O(n × k) | O(n × k) | O(n × k) | O(n + k) | 稳定 | 多位数排序(如手机号/日期) |