1. 引言
排序算法是计算机科学中最基础、最重要的算法类别之一,广泛应用于数据库查询、数据分析、搜索引擎、操作系统调度等各个领域。掌握常用排序算法的原理、实现和性能特点,是每个程序员必备的基本功。
本文将详细介绍十大常用排序算法,包括它们的核心思想、实现代码(Python/Java)、时间复杂度、空间复杂度、稳定性分析以及适用场景。无论你是准备面试、学习算法基础,还是需要在项目中选择合适的排序方法,这篇文章都将为你提供全面的参考。
2. 算法概览
在深入每个算法之前,我们先通过一个表格快速了解这十大排序算法的基本特性:
| 排序算法 | 平均时间复杂度 | 最坏时间复杂度 | 空间复杂度 | 稳定性 | 主要特点 |
|---|---|---|---|---|---|
| 冒泡排序 | O(n²) | O(n²) | O(1) | 稳定 | 简单直观,适合教学 |
| 选择排序 | O(n²) | O(n²) | O(1) | 不稳定 | 交换次数最少 |
| 插入排序 | O(n²) | O(n²) | O(1) | 稳定 | 对小规模数据高效 |
| 希尔排序 | O(n log n) | O(n²) | O(1) | 不稳定 | 插入排序的改进版 |
| 归并排序 | O(n log n) | O(n log n) | O(n) | 稳定 | 分治思想,稳定高效 |
| 快速排序 | O(n log n) | O(n²) | O(log n) | 不稳定 | 平均性能最好 |
| 堆排序 | O(n log n) | O(n log n) | O(1) | 不稳定 | 原地排序,适合大数据 |
| 计数排序 | O(n + k) | O(n + k) | O(k) | 稳定 | 非比较排序,整数专用 |
| 桶排序 | O(n + k) | O(n²) | O(n + k) | 稳定 | 均匀分布数据高效 |
| 基数排序 | O(n × k) | O(n × k) | O(n + k) | 稳定 | 多关键字排序 |
说明:n 为数据规模,k 为数据范围(计数/桶排序)或位数(基数排序)。
3. 冒泡排序 (Bubble Sort)
3.1 算法原理
冒泡排序通过重复遍历待排序序列,比较相邻元素,如果顺序错误就交换它们。每一轮遍历都会将当前未排序部分的最大元素"冒泡"到正确位置。
3.2 实现代码
python
def bubble_sort(arr):
n = len(arr)
for i in range(n - 1):
# 优化:如果某一轮没有发生交换,说明已排序完成
swapped = False
for j in range(n - 1 - i):
if arr[j] > arr[j + 1]:
arr[j], arr[j + 1] = arr[j + 1], arr[j]
swapped = True
if not swapped:
break
return arr
java
public class BubbleSort {
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;
}
}
}
3.3 性能分析
- 时间复杂度:平均 O(n²),最好 O(n)(已排序时),最坏 O(n²)
- 空间复杂度:O(1),原地排序
- 稳定性:稳定(相等元素不交换)
- 适用场景:教学演示、小规模数据、几乎已排序的数据
4. 选择排序 (Selection Sort)
4.1 算法原理
选择排序每次从未排序部分选择最小(或最大)元素,放到已排序部分的末尾。与冒泡排序相比,它减少了交换次数。
4.2 实现代码
python
def selection_sort(arr):
n = len(arr)
for i in range(n - 1):
min_idx = i
for j in range(i + 1, n):
if arr[j] < arr[min_idx]:
min_idx = j
if min_idx != i:
arr[i], arr[min_idx] = arr[min_idx], arr[i]
return arr
java
public class SelectionSort {
public static void selectionSort(int[] arr) {
int n = arr.length;
for (int i = 0; i < n - 1; i++) {
int minIdx = i;
for (int j = i + 1; j < n; j++) {
if (arr[j] < arr[minIdx]) {
minIdx = j;
}
}
if (minIdx != i) {
int temp = arr[i];
arr[i] = arr[minIdx];
arr[minIdx] = temp;
}
}
}
}
4.3 性能分析
- 时间复杂度:O(n²),无论数据如何分布
- 空间复杂度:O(1),原地排序
- 稳定性:不稳定(可能改变相等元素的相对顺序)
- 适用场景:交换成本较高的场景、小规模数据
5. 插入排序 (Insertion Sort)
5.1 算法原理
插入排序将待排序元素插入到已排序序列的适当位置,类似于整理扑克牌。对于几乎有序的数据,插入排序效率很高。
5.2 实现代码
python
def insertion_sort(arr):
n = len(arr)
for i in range(1, n):
key = arr[i]
j = i - 1
while j >= 0 and arr[j] > key:
arr[j + 1] = arr[j]
j -= 1
arr[j + 1] = key
return arr
java
public class InsertionSort {
public static void insertionSort(int[] arr) {
int n = arr.length;
for (int i = 1; i < n; i++) {
int key = arr[i];
int j = i - 1;
while (j >= 0 && arr[j] > key) {
arr[j + 1] = arr[j];
j--;
}
arr[j + 1] = key;
}
}
}
5.3 性能分析
- 时间复杂度:平均 O(n²),最好 O(n)(已排序时),最坏 O(n²)
- 空间复杂度:O(1),原地排序
- 稳定性:稳定
- 适用场景:小规模数据、几乎有序的数据、在线算法(数据流式输入)
6. 希尔排序 (Shell Sort)
6.1 算法原理
希尔排序是插入排序的改进版,通过将原始列表分割成多个子序列进行插入排序,逐渐减少增量直至为1。它利用了插入排序对几乎有序数据高效的特点。
6.2 实现代码
python
def shell_sort(arr):
n = len(arr)
gap = n // 2
while gap > 0:
for i in range(gap, n):
temp = arr[i]
j = i
while j >= gap and arr[j - gap] > temp:
arr[j] = arr[j - gap]
j -= gap
arr[j] = temp
gap //= 2
return arr
java
public class ShellSort {
public static void shellSort(int[] arr) {
int n = arr.length;
for (int gap = n / 2; gap > 0; gap /= 2) {
for (int i = gap; i < n; i++) {
int temp = arr[i];
int j = i;
while (j >= gap && arr[j - gap] > temp) {
arr[j] = arr[j - gap];
j -= gap;
}
arr[j] = temp;
}
}
}
}
6.3 性能分析
- 时间复杂度:取决于增量序列,平均 O(n log n),最坏 O(n²)
- 空间复杂度:O(1),原地排序
- 稳定性:不稳定
- 适用场景:中等规模数据,比简单插入排序更高效
7. 归并排序 (Merge Sort)
7.1 算法原理
归并排序采用分治策略:将数组递归地分成两半,分别排序,然后合并两个有序子数组。它是稳定排序算法中效率较高的。
7.2 实现代码
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
java
public class MergeSort {
public static void mergeSort(int[] arr) {
if (arr.length <= 1) return;
int[] temp = new int[arr.length];
mergeSort(arr, 0, arr.length - 1, temp);
}
private static void mergeSort(int[] arr, int left, int right, int[] temp) {
if (left >= right) return;
int mid = left + (right - left) / 2;
mergeSort(arr, left, mid, temp);
mergeSort(arr, mid + 1, right, temp);
merge(arr, left, mid, right, temp);
}
private static void merge(int[] arr, int left, int mid, int right, int[] temp) {
int i = left, j = mid + 1, k = left;
while (i <= mid && j <= right) {
if (arr[i] <= arr[j]) {
temp[k++] = arr[i++];
} else {
temp[k++] = arr[j++];
}
}
while (i <= mid) temp[k++] = arr[i++];
while (j <= right) temp[k++] = arr[j++];
System.arraycopy(temp, left, arr, left, right - left + 1);
}
}
7.3 性能分析
- 时间复杂度:O(n log n),稳定
- 空间复杂度:O(n),需要额外空间
- 稳定性:稳定
- 适用场景:需要稳定排序的大数据量、链表排序、外部排序
8. 快速排序 (Quick Sort)
8.1 算法原理
快速排序采用分治策略:选择一个基准元素,将数组分为两部分,左边都小于基准,右边都大于基准,然后递归地对两部分排序。
8.2 实现代码
python
def quick_sort(arr):
if len(arr) <= 1:
return arr
pivot = arr[len(arr) // 2]
left = [x for x in arr if x < pivot]
middle = [x for x in arr if x == pivot]
right = [x for x in arr if x > pivot]
return quick_sort(left) + middle + quick_sort(right)
# 原地分区版本
def quick_sort_inplace(arr, low=0, high=None):
if high is None:
high = len(arr) - 1
if low < high:
pi = partition(arr, low, high)
quick_sort_inplace(arr, low, pi - 1)
quick_sort_inplace(arr, pi + 1, high)
return arr
def partition(arr, low, high):
pivot = arr[high]
i = low - 1
for j in range(low, high):
if arr[j] <= pivot:
i += 1
arr[i], arr[j] = arr[j], arr[i]
arr[i + 1], arr[high] = arr[high], arr[i + 1]
return i + 1
java
public class QuickSort {
public static void quickSort(int[] arr) {
quickSort(arr, 0, arr.length - 1);
}
private 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;
}
}
8.3 性能分析
- 时间复杂度:平均 O(n log n),最坏 O(n²)(已排序或逆序时)
- 空间复杂度:O(log n)(递归栈)
- 稳定性:不稳定
- 适用场景:通用排序,平均性能最好,实际应用最广泛
9. 堆排序 (Heap Sort)
9.1 算法原理
堆排序利用堆这种数据结构:首先构建最大堆,然后重复将堆顶元素(最大值)与末尾元素交换,并调整堆,直到整个数组有序。
9.2 实现代码
python
def heap_sort(arr):
n = len(arr)
# 构建最大堆
for i in range(n // 2 - 1, -1, -1):
heapify(arr, n, i)
# 逐个提取元素
for i in range(n - 1, 0, -1):
arr[0], arr[i] = arr[i], arr[0] # 交换
heapify(arr, i, 0)
return arr
def heapify(arr, n, i):
largest = i
left = 2 * i + 1
right = 2 * i + 2
if left < n and arr[left] > arr[largest]:
largest = left
if right < n and arr[right] > arr[largest]:
largest = right
if largest != i:
arr[i], arr[largest] = arr[largest], arr[i]
heapify(arr, n, largest)
java
public class HeapSort {
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 temp = arr[i];
arr[i] = arr[largest];
arr[largest] = temp;
heapify(arr, n, largest);
}
}
}
9.3 性能分析
- 时间复杂度:O(n log n),稳定
- 空间复杂度:O(1),原地排序
- 稳定性:不稳定
- 适用场景:需要原地排序的大数据量、实时系统、内存受限环境
10. 计数排序 (Counting Sort)
10.1 算法原理
计数排序是非比较排序算法,适用于整数排序。它统计每个元素出现的次数,然后根据统计结果重建有序序列。
10.2 实现代码
python
def counting_sort(arr):
if not arr:
return arr
# 找到最大值和最小值
max_val = max(arr)
min_val = min(arr)
# 创建计数数组
count_size = max_val - min_val + 1
count = [0] * count_size
# 统计每个元素出现次数
for num in arr:
count[num - min_val] += 1
# 重建有序数组
result = []
for i in range(count_size):
result.extend([i + min_val] * count[i])
return result
java
public class CountingSort {
public static int[] countingSort(int[] arr) {
if (arr.length == 0) return arr;
int max = arr[0], min = arr[0];
for (int num : arr) {
if (num > max) max = num;
if (num < min) min = num;
}
int range = max - min + 1;
int[] count = new int[range];
for (int num : arr) {