-
- 排序基础概念与核心指标
- 1.1 什么是排序
- 1.2 稳定性详解
- 1.3 内部排序 vs 外部排序
-
- 插入类排序
- 2.1 直接插入排序
- 2.2 希尔排序(缩小增量排序)
-
- 选择类排序
- 3.1 直接选择排序
- 3.2 堆排序
-
- 交换类排序
- 4.1 冒泡排序
- 4.2 快速排序(递归 + 非递归 + 优化)
-
- 归并排序(递归 + 非递归)
-
- 非比较类排序:计数排序
-
- 所有排序复杂度 + 稳定性终极对比表
-
- 面试高频真题 + 答案解析
1. 排序基础概念与核心指标
1.1 排序定义
将一组数据按照关键字大小,递增或递减排列的操作。
1.2 稳定性(面试必考)
- 稳定排序:相等元素在排序后相对位置不变
- 不稳定排序:相等元素在排序后相对位置可能改变
意义:多关键字排序(如先按成绩再按学号)必须保证稳定。
1.3 内部排序 vs 外部排序
- 内部排序:数据全部放内存,速度快
- 外部排序:数据太大无法全放入内存,需要磁盘交互(常用归并排序)
2. 插入类排序
2.1 直接插入排序
核心思想
像摸扑克牌一样,把当前数字插入到前面已经排好序的区间里。
复杂度深度分析
- 最好时间复杂度:O (N)(数组已有序,只需遍历一遍)
- 最坏 / 平均时间复杂度:O (N²)(逆序时,每个数都要挪到底)
- 空间复杂度:O (1)(仅用临时变量,无额外空间)
- 稳定性:稳定
- 适用场景:数据量小、数据基本有序
Java 实现代码
public static void insertSort(int[] array) {
for (int i = 1; i < array.length; i++) {
int tmp = array[i];
int j = i - 1;
for (; j >= 0; j--) {
if(array[j] > tmp) {
array[j + 1] = array[j];
}else {
break;
}
}
array[j + 1] = tmp;
}
}
2.2 希尔排序(缩小增量排序)
核心思想
分组预排序 → 逐步缩小增量 → 最后执行直接插入排序,是直接插入的高效优化版。
复杂度深度分析
- 时间复杂度:O (N^1.3) ~ O (N^1.5)(和增量序列相关,无法精确数学计算)
- 空间复杂度:O(1)
- 稳定性:不稳定(分组跳跃交换会破坏相对位置)
Java 实现代码
public static void shellSort(int[] array) {
int gap = array.length;
while (gap > 1) {
gap /= 2;
shell(array, gap);
}
}
public static void shell(int[] array, int gap) {
for (int i = gap; i < array.length; i++) {
int tmp = array[i];
int j = i - gap;
for (; j >= 0; j -= gap) {
if(array[j] > tmp) {
array[j + gap] = array[j];
}else {
break;
}
}
array[j + gap] = tmp;
}
}
3. 选择类排序
3.1 直接选择排序
核心思想
每一轮选出最小值,放到已排序区间末尾。
复杂度深度分析
- 时间复杂度:最好、最坏、平均都是 O (N²)(无论有序与否都要完整比较)
- 空间复杂度:O(1)
- 稳定性:不稳定(交换会打乱相同元素顺序)
Java 实现代码
public static void selectSort(int[] array) {
for (int i = 0; i < array.length; i++) {
int minIndex = i;
for (int j = i + 1; j < array.length; j++) {
if(array[j] < array[minIndex]) {
minIndex = j;
}
}
swap(array, i, minIndex);
}
}
private static void swap(int[] array, int i, int j) {
int tmp = array[i];
array[i] = array[j];
array[j] = tmp;
}
3.2 堆排序
核心思想
利用大顶堆 / 小顶堆高效选择最大值,升序建大堆,降序建小堆。
复杂度深度分析
- 时间复杂度:最好、最坏、平均都是 O (N log N)(建堆 O (N),调整 O (N log N))
- 空间复杂度:O (1)(原地排序,无递归栈 / 额外数组)
- 稳定性:不稳定
Java 实现代码
public static void heapSort(int[] array) {
createHeap(array);
int end = array.length - 1;
while (end > 0) {
swap(array, 0, end);
siftDown(array, 0, end);
end--;
}
}
private static void createHeap(int[] array) {
for (int parent = (array.length - 2) / 2; parent >= 0; parent--) {
siftDown(array, parent, array.length);
}
}
private static void siftDown(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(array, child, parent);
parent = child;
child = 2 * parent + 1;
} else {
break;
}
}
}
4. 交换类排序
4.1 冒泡排序
核心思想
相邻两两比较交换,大数 “沉底”,小数 “上浮”。
复杂度深度分析
- 最好时间复杂度:O (N)(加标志位,有序时直接退出)
- 最坏 / 平均时间复杂度:O(N²)
- 空间复杂度:O(1)
- 稳定性:稳定
Java 实现代码
public static void bubbleSort(int[] array) {
for (int i = 0; i < array.length - 1; i++) {
boolean flg = false;
for (int j = 0; j < array.length - 1 - i; j++) {
if(array[j] > array[j + 1]) {
swap(array, j, j + 1);
flg = true;
}
}
if(!flg) {
return;
}
}
}
4.2 快速排序
核心思想
分治法:选基准 → 分区 → 递归左右。工程常用优化:三数取中、小数据用插入排序、非递归实现。
复杂度深度分析
- 最好 / 平均时间复杂度:O (N log N)(每次均分,递归树高度 log N)
- 最坏时间复杂度:O (N²)(数据已有序,退化为链表)
- 空间复杂度:O (log N) ~ O (N)(递归调用栈)
- 稳定性:不稳定
Java 实现(递归 + 非递归)
// 递归快排
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;
// 小范围使用插入排序优化
if(end - start + 1 <= 8) {
insertSortRange(array, start, end);
return;
}
// 三数取中优化
int index = threeMid(array, start, end);
swap(array, start, index);
int par = parttion(array, start, end);
quick(array, start, par - 1);
quick(array, par + 1, end);
}
// 非递归快排
public static void quickSortNor(int[] array) {
Deque<Integer> stack = new LinkedList<>();
int start = 0;
int end = array.length - 1;
stack.push(start);
stack.push(end);
while (!stack.isEmpty()) {
end = stack.pop();
start = stack.pop();
int par = parttion2(array, start, end);
if(par > start + 1) {
stack.push(start);
stack.push(par - 1);
}
if(par < end - 1) {
stack.push(par + 1);
stack.push(end);
}
}
}
// 前后指针分区
private static int parttion(int[] array, int left, int right) {
int prev = left;
int cur = left + 1;
while (cur <= right) {
if(array[cur] < array[left] && array[++prev] != array[cur]) {
swap(array, cur, prev);
}
cur++;
}
swap(array, prev, left);
return prev;
}
5. 归并排序
核心思想
分治法:分解 → 排序子段 → 归并。最适合外部排序 / 海量数据排序。
复杂度深度分析
- 时间复杂度:最好、最坏、平均都是 O (N log N)
- 空间复杂度:O (N)(需要临时数组存归并结果)
- 稳定性:稳定(唯一 O (N log N) 稳定排序)
Java 实现(递归 + 非递归)
// 递归归并
public static void mergeSort1(int[] array) {
mergeSortChild(array, 0, array.length - 1);
}
private static void mergeSortChild(int[] array, int left, int right) {
if(left >= right) return;
int mid = (left + right) / 2;
mergeSortChild(array, left, mid);
mergeSortChild(array, mid + 1, right);
merge(array, left, mid, right);
}
// 非递归归并
public static void mergeSort(int[] array) {
int gap = 1;
while (gap < array.length) {
for (int i = 0; i < array.length; i += 2 * gap) {
int left = i;
int mid = left + gap - 1;
mid = Math.min(mid, array.length - 1);
int right = mid + gap;
right = Math.min(right, array.length - 1);
merge(array, left, mid, right);
}
gap *= 2;
}
}
// 归并核心
private static void merge(int[] array, int left, int mid, int right) {
int[] tmp = new int[right - left + 1];
int k = 0;
int s1 = left;
int s2 = mid + 1;
while (s1 <= mid && s2 <= right) {
if(array[s1] <= array[s2]) {
tmp[k++] = array[s1++];
}else {
tmp[k++] = array[s2++];
}
}
while (s1 <= mid) tmp[k++] = array[s1++];
while (s2 <= right) tmp[k++] = array[s2++];
for (int i = 0; i < tmp.length; i++) {
array[i + left] = tmp[i];
}
}
6. 非比较类排序:计数排序
核心思想
不比较元素,只统计次数,再按计数回填数组。
复杂度深度分析
- 时间复杂度:O (N + 数据范围)
- 空间复杂度:O (数据范围)
- 稳定性:稳定
- 适用场景:分数、年龄、频次统计(范围小且集中)
Java 实现代码
public static void countSort(int[] array) {
int max = array[0];
int min = array[0];
for (int num : array) {
max = Math.max(max, num);
min = Math.min(min, num);
}
int range = max - min + 1;
int[] count = new int[range];
for (int num : array) {
count[num - min]++;
}
int k = 0;
for (int i = 0; i < count.length; i++) {
while (count[i] > 0) {
array[k++] = i + min;
count[i]--;
}
}
}
7. 排序算法终极对比表(面试直接背)
表格
| 排序算法 | 最好时间 | 最坏时间 | 空间 | 稳定性 |
|---|---|---|---|---|
| 直接插入 | O(N) | O(N²) | O(1) | ✅ 稳定 |
| 希尔 | O(N) | O(N²) | O(1) | ❌ 不稳定 |
| 直接选择 | O(N²) | O(N²) | O(1) | ❌ 不稳定 |
| 堆排序 | O(N log N) | O(N log N) | O(1) | ❌ 不稳定 |
| 冒泡 | O(N) | O(N²) | O(1) | ✅ 稳定 |
| 快速排序 | O(N log N) | O(N²) | O(log N) | ❌ 不稳定 |
| 归并排序 | O(N log N) | O(N log N) | O(N) | ✅ 稳定 |
| 计数排序 | O (N + 范围) | O (N + 范围) | O (范围) | ✅ 稳定 |
8. 面试高频真题(含答案)
- 快速排序基于什么思想?分治法
- 稳定且时间复杂度 O (N²) 的排序?冒泡、直接插入
- 占用 O (N) 辅助空间的排序是?归并排序
- 数据基本有序时最快的排序?直接插入排序
-
海量数据外部排序首选?归并排序

70

被折叠的 条评论
为什么被折叠?



