以下是计算机科学中最经典的排序算法,涵盖原理、时间复杂度、适用场景及代码实现(C++/Python),适合面试备考和算法学习。
1. 排序算法概览
排序算法 | 平均时间复杂度 | 最好情况 | 最坏情况 | 空间复杂度 | 稳定性 | 核心思想 |
冒泡排序 | 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 log² n) | O(n²) | O(1) | 不稳定 | 分组插入排序 |
归并排序 | O(n log n) | O(n log n) | O(n log n) | O(n) | 稳定 | 分治法 + 合并有序数组 |
快速排序 | 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(1) | 不稳定 | 构建堆 + 交换堆顶元素 |
计数排序 | O(n + k) | O(n + k) | O(n + k) | O(k) | 稳定 | 统计元素出现次数 |
桶排序 | O(n + k) | O(n + k) | O(n²) | O(n + k) | 稳定 | 分桶后各自排序 |
基数排序 | O(n × k) | O(n × k) | O(n × k) | O(n + k) | 稳定 | 按位比较(个→十→百) |
注:
- k 为数据范围(计数排序)或桶的数量(桶排序)。
- 稳定性:相等元素的相对顺序是否改变。
2. 重点排序算法详解
(1) 快速排序(Quick Sort)
原理:
- 选择一个基准值(pivot)(通常为第一个元素)。
- 分区:将小于基准的放左边,大于的放右边。
- 递归处理左右子数组。
C++实现:
int partition(vector<int>& arr, int low, int high) {
int pivot = arr[low];
while (low < high) {
while (low < high && arr[high] >= pivot) high--;
arr[low] = arr[high];
while (low < high && arr[low] <= pivot) low++;
arr[high] = arr[low];
}
arr[low] = pivot;
return low;
}
void quickSort(vector<int>& arr, int low, int high) {
if (low < high) {
int pivotPos = partition(arr, low, high);
quickSort(arr, low, pivotPos - 1);
quickSort(arr, pivotPos + 1, high);
}
}
特点:
- 平均性能最优,但最坏情况(如已排序数组)退化为O(n²)。
- 优化:随机选择基准值(
swap(arr[low], arr[rand() % (high-low+1) + low])
)。
(2) 归并排序(Merge Sort)
原理:
- 分治:将数组递归分成两半。
- 合并:将两个有序数组合并为一个。
Python实现:
def merge_sort(arr):
if len(arr) <= 1:
return arr
mid = len(arr) // 2
left = merge_sort(arr[:mid])
right = merge_sort(arr[mid:])
return merge(left, right)
def merge(left, right):
result = []
i = j = 0
while i < len(left) and j < len(right):
if left[i] < right[j]:
result.append(left[i])
i += 1
else:
result.append(right[j])
j += 1
result.extend(left[i:])
result.extend(right[j:])
return result
特点:
- 稳定排序,适合链表排序。
- 需要额外O(n)空间。
(3) 堆排序(Heap Sort)
原理:
- 建堆:将数组调整为最大堆。
- 排序:交换堆顶与末尾元素,调整剩余堆。
C++实现:
void heapify(vector<int>& arr, int n, int i) {
int largest = i, left = 2 * i + 1, 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) {
swap(arr[i], arr[largest]);
heapify(arr, n, largest);
}
}
void heapSort(vector<int>& arr) {
for (int i = arr.size() / 2 - 1; i >= 0; i--)
heapify(arr, arr.size(), i);
for (int i = arr.size() - 1; i > 0; i--) {
swap(arr[0], arr[i]);
heapify(arr, i, 0);
}
}
特点:
- 原地排序,适合大数据量。
- 不稳定(堆调整可能改变相等元素顺序)。
3. 非比较排序
(1) 计数排序(Counting Sort)
适用场景:整数且范围较小(如0~100的考试成绩)。
步骤:
- 统计每个元素的出现次数。
- 计算前缀和确定位置。
- 反向填充结果数组。
Python实现:
def counting_sort(arr):
max_val = max(arr)
count = [0] * (max_val + 1)
for num in arr:
count[num] += 1
idx = 0
for i in range(max_val + 1):
while count[i] > 0:
arr[idx] = i
idx += 1
count[i] -= 1
4. 如何选择排序算法?
场景 | 推荐算法 | 理由 |
小规模数据(n < 50) | 插入排序 | 常数项小,无递归开销 |
大规模随机数据 | 快速排序 | 平均O(n log n),缓存友好 |
需要稳定性 | 归并排序 | 稳定且O(n log n) |
数据范围有限 | 计数排序 | O(n + k)线性时间 |
内存受限 | 堆排序 | 原地排序,无需额外空间 |
5. 记忆口诀
- 快排:“基准分两边,递归排子集。”
- 归并:“分治再合并,稳定保顺序。”
- 堆排:“建堆再交换,原地省空间。”
- 计数:“统计频次再填充,整数小范围最优选。”