01-排序算法
💡 核心结论
排序算法本质
目标:将无序数据按某种规则排列成有序
关键指标:时间复杂度、空间复杂度、稳定性
稳定性:相等元素排序后相对位置不变
选择:根据数据规模、是否原地、是否稳定来选择
算法对比(必须记住)
算法 |
平均 |
最坏 |
最好 |
空间 |
稳定 |
适用场景 |
|---|---|---|---|---|---|---|
冒泡 |
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^1.3) |
O(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²) |
O(n log 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²) |
O(n) |
O(n+k) |
✅ |
均匀分布 |
基数 |
O(d(n+k)) |
O(d(n+k)) |
O(d(n+k)) |
O(n+k) |
✅ |
多关键字 |
选择建议
通用场景:快速排序(平均最快)
需要稳定:归并排序
空间受限:堆排序(原地)
小规模:插入排序(常数小)
近似有序:插入排序
整数范围小:计数排序
链表排序:归并排序
核心思想
比较排序:通过比较决定顺序,下界O(n log n)
非比较排序:利用特殊性质,可以O(n)
分治思想:归并、快排
减治思想:插入、选择
堆结构:堆排序
🔵 冒泡排序
原理
重复遍历,比较相邻元素,逆序则交换,每次把最大元素”冒泡”到末尾。
时间复杂度
最好:O(n) - 已有序,一次遍历
平均:O(n²)
最坏:O(n²) - 逆序
优化
添加flag:一轮没交换则提前结束
记录最后交换位置:减少比较次数
🔴 选择排序
原理
每次从未排序部分选择最小元素,放到已排序部分末尾。
时间复杂度
始终O(n²),无论数据状态
交换次数O(n),少于冒泡
🟡 插入排序
原理
将元素插入到已排序部分的正确位置。
时间复杂度
最好:O(n) - 已有序
平均:O(n²)
适合小规模或近似有序数据
🟢 归并排序
原理
分治策略:分割→递归排序→合并。
特点
稳定排序
时间稳定:O(n log n)
空间O(n):需要额外数组
适合链表:不需要随机访问
应用
外部排序
求逆序对
链表排序
🟣 快速排序
原理
选择pivot,分区使左边<pivot,右边>pivot,递归排序。
特点
平均最快:O(n log n)
最坏O(n²):已排序数据,需要随机化或三数取中
原地排序:空间O(log n)递归栈
不稳定
优化
三数取中选pivot
小规模用插入排序
三路快排处理重复元素
🟠 堆排序
原理
建最大堆,反复取堆顶放到末尾,调整堆。
特点
时间稳定:O(n log n)
原地排序:O(1)额外空间
不稳定
⚪ 非比较排序
计数排序
适用:整数,范围k不大(k < n log n)
时间:O(n + k)
空间:O(k)
稳定
桶排序
适用:数据均匀分布
时间:O(n)(理想情况)
空间:O(n + k)
基数排序
适用:多关键字排序
时间:O(d(n + k)),d为位数
空间:O(n + k)
🎯 经典应用
1. 第K大元素
# 快速选择:O(n)平均
def findKthLargest(nums, k):
return quickSelect(nums, len(nums) - k)
# 堆:O(n log k)
def findKthLargest(nums, k):
return heapq.nlargest(k, nums)[-1]
2. 数组中的第K个最大元素
快速选择 > 堆 > 排序
3. 合并K个有序链表
最小堆:O(n log k)
4. 求逆序对
归并排序:O(n log n)
📚 LeetCode练习
基础
应用
💡 面试建议
必须手写
快速排序
归并排序
堆排序
必须理解
时间复杂度分析
稳定性的应用
优化技巧
常见问题
为什么快排平均最快?
缓存友好、原地排序、常数因子小
什么时候用归并?
需要稳定性、链表排序、外部排序
堆排序的优势?
原地、最坏O(n log n)、适合Top K
🔧 优化技巧
1. 小规模用插入
if len(arr) < 10:
insertion_sort(arr)
2. 快排优化
随机化pivot
三数取中
三路快排
3. 混合排序
# 归并排序 + 插入排序
def merge_sort(arr):
if len(arr) < 10:
return insertion_sort(arr)
# 继续归并
🎯 总结
排序是算法基础,务必掌握:
理解原理:每种排序的核心思想
手写实现:至少快排、归并、堆排序
复杂度分析:时间、空间、稳定性
应用场景:知道何时用何种排序
优化技巧:混合排序、随机化等
💻 完整代码实现
Python 实现
1"""
2经典排序算法实现
3"""
4
5def bubble_sort(arr):
6 """冒泡排序:O(n²)
7 重复遍历,相邻元素逆序则交换
8 """
9 n = len(arr)
10 for i in range(n):
11 swapped = False
12 for j in range(n - i - 1):
13 if arr[j] > arr[j + 1]:
14 arr[j], arr[j + 1] = arr[j + 1], arr[j]
15 swapped = True
16 if not swapped: # 优化:一轮没交换则已有序
17 break
18 return arr
19
20
21def selection_sort(arr):
22 """选择排序:O(n²)
23 每次选择最小元素放到已排序部分末尾
24 """
25 n = len(arr)
26 for i in range(n):
27 min_idx = i
28 for j in range(i + 1, n):
29 if arr[j] < arr[min_idx]:
30 min_idx = j
31 arr[i], arr[min_idx] = arr[min_idx], arr[i]
32 return arr
33
34
35def insertion_sort(arr):
36 """插入排序:O(n²)
37 将元素插入到已排序部分的正确位置
38 """
39 for i in range(1, len(arr)):
40 key = arr[i]
41 j = i - 1
42 while j >= 0 and arr[j] > key:
43 arr[j + 1] = arr[j]
44 j -= 1
45 arr[j + 1] = key
46 return arr
47
48
49def merge_sort(arr):
50 """归并排序:O(n log n)
51 分治策略:分割→递归排序→合并
52 """
53 if len(arr) <= 1:
54 return arr
55
56 mid = len(arr) // 2
57 left = merge_sort(arr[:mid])
58 right = merge_sort(arr[mid:])
59
60 return merge(left, right)
61
62
63def merge(left, right):
64 """合并两个有序数组"""
65 result = []
66 i = j = 0
67
68 while i < len(left) and j < len(right):
69 if left[i] <= right[j]:
70 result.append(left[i])
71 i += 1
72 else:
73 result.append(right[j])
74 j += 1
75
76 result.extend(left[i:])
77 result.extend(right[j:])
78 return result
79
80
81def quick_sort(arr):
82 """快速排序:O(n log n)平均
83 选择pivot,分区,递归
84 """
85 if len(arr) <= 1:
86 return arr
87
88 pivot = arr[len(arr) // 2]
89 left = [x for x in arr if x < pivot]
90 middle = [x for x in arr if x == pivot]
91 right = [x for x in arr if x > pivot]
92
93 return quick_sort(left) + middle + quick_sort(right)
94
95
96def quick_sort_inplace(arr, low=0, high=None):
97 """原地快速排序"""
98 if high is None:
99 high = len(arr) - 1
100
101 if low < high:
102 pi = partition(arr, low, high)
103 quick_sort_inplace(arr, low, pi - 1)
104 quick_sort_inplace(arr, pi + 1, high)
105
106 return arr
107
108
109def partition(arr, low, high):
110 """分区函数"""
111 pivot = arr[high]
112 i = low - 1
113
114 for j in range(low, high):
115 if arr[j] <= pivot:
116 i += 1
117 arr[i], arr[j] = arr[j], arr[i]
118
119 arr[i + 1], arr[high] = arr[high], arr[i + 1]
120 return i + 1
121
122
123def heap_sort(arr):
124 """堆排序:O(n log n)
125 建最大堆,反复取堆顶
126 """
127 n = len(arr)
128
129 # 建堆
130 for i in range(n // 2 - 1, -1, -1):
131 heapify(arr, n, i)
132
133 # 排序
134 for i in range(n - 1, 0, -1):
135 arr[0], arr[i] = arr[i], arr[0]
136 heapify(arr, i, 0)
137
138 return arr
139
140
141def heapify(arr, n, i):
142 """堆调整"""
143 largest = i
144 left = 2 * i + 1
145 right = 2 * i + 2
146
147 if left < n and arr[left] > arr[largest]:
148 largest = left
149 if right < n and arr[right] > arr[largest]:
150 largest = right
151
152 if largest != i:
153 arr[i], arr[largest] = arr[largest], arr[i]
154 heapify(arr, n, largest)
155
156
157def counting_sort(arr):
158 """计数排序:O(n + k)
159 适用于整数,范围不大
160 """
161 if not arr:
162 return arr
163
164 min_val, max_val = min(arr), max(arr)
165 range_size = max_val - min_val + 1
166 count = [0] * range_size
167
168 # 计数
169 for num in arr:
170 count[num - min_val] += 1
171
172 # 累加
173 for i in range(1, range_size):
174 count[i] += count[i - 1]
175
176 # 输出
177 output = [0] * len(arr)
178 for num in reversed(arr): # 逆序保证稳定性
179 index = count[num - min_val] - 1
180 output[index] = num
181 count[num - min_val] -= 1
182
183 return output
184
185
186def test_sorting_algorithms():
187 """测试所有排序算法"""
188 import random
189 import time
190
191 test_cases = [
192 ([5, 2, 8, 1, 9], "小规模随机"),
193 ([1, 2, 3, 4, 5], "已排序"),
194 ([5, 4, 3, 2, 1], "逆序"),
195 ([3, 3, 2, 1, 2], "重复元素"),
196 (list(range(1000, 0, -1)), "大规模逆序")
197 ]
198
199 algorithms = [
200 ("冒泡排序", bubble_sort),
201 ("选择排序", selection_sort),
202 ("插入排序", insertion_sort),
203 ("归并排序", merge_sort),
204 ("快速排序", quick_sort),
205 ("原地快排", lambda arr: quick_sort_inplace(arr.copy())),
206 ("堆排序", heap_sort),
207 ("计数排序", counting_sort)
208 ]
209
210 print("=== 排序算法测试 ===\n")
211
212 for arr, desc in test_cases[:4]: # 跳过大规模测试
213 print(f"测试用例: {desc}")
214 print(f"原数组: {arr[:10]}{'...' if len(arr) > 10 else ''}")
215
216 for name, func in algorithms:
217 test_arr = arr.copy()
218 start = time.time()
219 result = func(test_arr)
220 elapsed = time.time() - start
221
222 is_sorted = result == sorted(arr)
223 status = "✅" if is_sorted else "❌"
224 print(f"{status} {name:12s}: {elapsed*1000:.3f}ms")
225
226 print()
227
228
229if __name__ == '__main__':
230 test_sorting_algorithms()
231
C++ 实现
1/**
2 * 经典排序算法实现
3 */
4
5#include <iostream>
6#include <vector>
7#include <algorithm>
8
9using namespace std;
10
11// 冒泡排序
12void bubbleSort(vector<int>& arr) {
13 int n = arr.size();
14 for (int i = 0; i < n; i++) {
15 bool swapped = false;
16 for (int j = 0; j < n - i - 1; j++) {
17 if (arr[j] > arr[j + 1]) {
18 swap(arr[j], arr[j + 1]);
19 swapped = true;
20 }
21 }
22 if (!swapped) break;
23 }
24}
25
26// 选择排序
27void selectionSort(vector<int>& arr) {
28 int n = arr.size();
29 for (int i = 0; i < n; i++) {
30 int minIdx = i;
31 for (int j = i + 1; j < n; j++) {
32 if (arr[j] < arr[minIdx]) {
33 minIdx = j;
34 }
35 }
36 swap(arr[i], arr[minIdx]);
37 }
38}
39
40// 插入排序
41void insertionSort(vector<int>& arr) {
42 int n = arr.size();
43 for (int i = 1; i < n; i++) {
44 int key = arr[i];
45 int j = i - 1;
46 while (j >= 0 && arr[j] > key) {
47 arr[j + 1] = arr[j];
48 j--;
49 }
50 arr[j + 1] = key;
51 }
52}
53
54// 归并排序
55void merge(vector<int>& arr, int left, int mid, int right) {
56 vector<int> L(arr.begin() + left, arr.begin() + mid + 1);
57 vector<int> R(arr.begin() + mid + 1, arr.begin() + right + 1);
58
59 int i = 0, j = 0, k = left;
60
61 while (i < L.size() && j < R.size()) {
62 if (L[i] <= R[j]) {
63 arr[k++] = L[i++];
64 } else {
65 arr[k++] = R[j++];
66 }
67 }
68
69 while (i < L.size()) arr[k++] = L[i++];
70 while (j < R.size()) arr[k++] = R[j++];
71}
72
73void mergeSortHelper(vector<int>& arr, int left, int right) {
74 if (left < right) {
75 int mid = left + (right - left) / 2;
76 mergeSortHelper(arr, left, mid);
77 mergeSortHelper(arr, mid + 1, right);
78 merge(arr, left, mid, right);
79 }
80}
81
82void mergeSort(vector<int>& arr) {
83 mergeSortHelper(arr, 0, arr.size() - 1);
84}
85
86// 快速排序
87int partition(vector<int>& arr, int low, int high) {
88 int pivot = arr[high];
89 int i = low - 1;
90
91 for (int j = low; j < high; j++) {
92 if (arr[j] <= pivot) {
93 i++;
94 swap(arr[i], arr[j]);
95 }
96 }
97 swap(arr[i + 1], arr[high]);
98 return i + 1;
99}
100
101void quickSortHelper(vector<int>& arr, int low, int high) {
102 if (low < high) {
103 int pi = partition(arr, low, high);
104 quickSortHelper(arr, low, pi - 1);
105 quickSortHelper(arr, pi + 1, high);
106 }
107}
108
109void quickSort(vector<int>& arr) {
110 quickSortHelper(arr, 0, arr.size() - 1);
111}
112
113// 堆排序
114void heapify(vector<int>& arr, int n, int i) {
115 int largest = i;
116 int left = 2 * i + 1;
117 int right = 2 * i + 2;
118
119 if (left < n && arr[left] > arr[largest])
120 largest = left;
121 if (right < n && arr[right] > arr[largest])
122 largest = right;
123
124 if (largest != i) {
125 swap(arr[i], arr[largest]);
126 heapify(arr, n, largest);
127 }
128}
129
130void heapSort(vector<int>& arr) {
131 int n = arr.size();
132
133 // 建堆
134 for (int i = n / 2 - 1; i >= 0; i--) {
135 heapify(arr, n, i);
136 }
137
138 // 排序
139 for (int i = n - 1; i > 0; i--) {
140 swap(arr[0], arr[i]);
141 heapify(arr, i, 0);
142 }
143}
144
145// 打印数组
146void printArray(const vector<int>& arr, const string& name) {
147 cout << name << ": [";
148 for (size_t i = 0; i < arr.size(); i++) {
149 cout << arr[i];
150 if (i < arr.size() - 1) cout << ", ";
151 }
152 cout << "]" << endl;
153}
154
155int main() {
156 cout << "=== 排序算法演示 ===" << endl << endl;
157
158 vector<int> original = {5, 2, 8, 1, 9, 3, 7};
159
160 vector<int> arr = original;
161 bubbleSort(arr);
162 printArray(arr, "冒泡排序");
163
164 arr = original;
165 selectionSort(arr);
166 printArray(arr, "选择排序");
167
168 arr = original;
169 insertionSort(arr);
170 printArray(arr, "插入排序");
171
172 arr = original;
173 mergeSort(arr);
174 printArray(arr, "归并排序");
175
176 arr = original;
177 quickSort(arr);
178 printArray(arr, "快速排序");
179
180 arr = original;
181 heapSort(arr);
182 printArray(arr, "堆排序");
183
184 return 0;
185}