一、江湖风云起:为什么要学排序算法?
在编程的江湖中,排序算法就像是大侠们的基本功。无论是处理海量数据、优化搜索算法,还是开发游戏,排序算法都无处不在。今天,我们要探讨的是排序江湖中最基础的三种算法:插入排序、选择排序和冒泡排序。它们就像是江湖中的三位剑客,各有各的招式和心法。
二、插入排序:像玩扑克牌一样排序
1. 核心心法:步步为营
插入排序的核心思想就像你在玩扑克牌时整理手牌的过程。每次拿到一张新牌,你都会将它插入到已排好序的牌中合适的位置。这种增量式排序的方法,让数组逐步变得有序。
类比江湖:这就像是一位武林新手,每学到一招新功夫,都会融入自己的招式体系中,让自己的武功越来越强。
2. 招式拆解
让我们用一个例子来拆解插入排序的招式。假设我们有一个数组:[87, 35, 67, 33, 22]
,我们要将它按升序排列。
第 1 回合(处理 35
):
- 已排序区间:
[87]
(只有一个元素,自然有序) - 待插入元素:
35
- 比较过程:
35
vs87
→87 > 35
→ 后移87
→ 空位在索引 0- 插入
35
到索引 0
- 结果:
[35, 87, 67, 33, 22]
第 2 回合(处理 67
):
- 已排序区间:
[35, 87]
- 待插入元素:
67
- 比较过程:
67
vs87
→87 > 67
→ 后移87
→ 空位在索引 167
vs35
→35 < 67
→ 停止比较- 插入
67
到索引 1
- 结果:
[35, 67, 87, 33, 22]
第 3 回合(处理 33
):
- 已排序区间:
[35, 67, 87]
- 待插入元素:
33
- 比较过程:
33
vs87
→ 后移87
33
vs67
→ 后移67
33
vs35
→ 后移35
- 空位在索引 0,插入
33
- 结果:
[33, 35, 67, 87, 22]
第 4 回合(处理 22
):
- 已排序区间:
[33, 35, 67, 87]
- 待插入元素:
22
- 比较过程:
22
vs87
→ 后移87
22
vs67
→ 后移67
22
vs35
→ 后移35
22
vs33
→ 后移33
- 空位在索引 0,插入
22
- 最终结果:
[22, 33, 35, 67, 87]
3. 代码实现(江湖秘籍)
#include <stdio.h>
void main() {
int a[5] = {87, 35, 67, 33, 22}, j, i, t;
// 江湖侠客修炼心法
for (i = 1; i < 5; i++) {
t = a[i]; // 取出待插入的"新招式"
// 在已掌握的招式中寻找合适位置
for (j = i - 1; j >= 0 && a[j] > t; j--)
a[j + 1] = a[j]; // 招式后移,腾出位置
a[j + 1] = t; // 插入新招式,形成更强的招式链
}
// 展示修炼成果
for (i = 0; i < 5; i++)
printf("%4d", a[i]);
getch();
}
三、选择排序:像挑水果一样找最小值
1. 核心心法:精准选择
选择排序的核心思想就像你在水果店挑选水果。每次你都会从所有水果中选出最好的一个,然后放在一边,接着再从剩下的水果中选出最好的,直到挑完所有水果。
类比江湖:这就像是一位武林高手在众多弟子中挑选最优秀的,每次选出一个,培养成精英,逐步壮大自己的门派。
2. 招式拆解
还是用数组 [87, 35, 67, 33, 22]
来演示选择排序的招式。
第 1 回合(i=0):
- 未排序区间:
[87, 35, 67, 33, 22]
- 寻找最小值:
22
(索引 4) - 交换:
87
↔22
- 结果:
[22, 35, 67, 33, 87]
第 2 回合(i=1):
- 未排序区间:
[35, 67, 33, 87]
- 寻找最小值:
33
(索引 3) - 交换:
35
↔33
- 结果:
[22, 33, 67, 35, 87]
第 3 回合(i=2):
- 未排序区间:
[67, 35, 87]
- 寻找最小值:
35
(索引 3) - 交换:
67
↔35
- 结果:
[22, 33, 35, 67, 87]
第 4 回合(i=3):
- 未排序区间:
[67, 87]
- 寻找最小值:
67
(索引 3) - 无需交换
- 最终结果:
[22, 33, 35, 67, 87]
3. 代码实现(江湖秘籍)
c
#include <stdio.h>
void main() {
int a[5] = {87, 35, 67, 33, 22}, j, i, t, k;
// 江湖掌门挑选精英弟子
for (i = 0; i < 5; i++) {
t = i; // 记录当前认为的"精英弟子"下标
// 遍历所有弟子,寻找真正的精英
for (j = i + 1; j < 5; j++)
if (a[t] > a[j])
t = j; // 更新精英弟子下标
// 如果发现更优秀的弟子,进行位置调整
if (t != i) {
k = a[t];
a[t] = a[i];
a[i] = k; // 交换位置,让精英弟子站在前面
}
}
// 展示精英弟子阵容
for (i = 0; i < 5; i++)
printf("%4d", a[i]);
getch();
}
四、冒泡排序:像气泡上浮一样排序
1. 核心心法:两两比较
冒泡排序的核心思想就像水中的气泡。大的气泡会更快地浮到水面,而小的气泡则会慢慢上浮。通过不断比较相邻元素,将较大的元素逐步 "冒泡" 到数组末尾。
类比江湖:这就像是江湖中的比武大会,每次让相邻的两位侠客比试,胜者晋级,败者后退,最终最强的侠客会脱颖而出。
2. 招式拆解
继续用数组 [87, 35, 67, 33, 22]
来演示冒泡排序的招式。
第 1 回合(i=0):
- 比较次数:4 次
- 过程:
87
vs35
→ 交换 →[35, 87, 67, 33, 22]
87
vs67
→ 交换 →[35, 67, 87, 33, 22]
87
vs33
→ 交换 →[35, 67, 33, 87, 22]
87
vs22
→ 交换 →[35, 67, 33, 22, 87]
- 结果:最大元素
87
像气泡一样 "浮" 到了末尾
第 2 回合(i=1):
- 比较次数:3 次
- 过程:
35
vs67
→ 不交换 →[35, 67, 33, 22, 87]
67
vs33
→ 交换 →[35, 33, 67, 22, 87]
67
vs22
→ 交换 →[35, 33, 22, 67, 87]
- 结果:第二大元素
67
浮到了倒数第二位置
第 3 回合(i=2):
- 比较次数:2 次
- 过程:
35
vs33
→ 交换 →[33, 35, 22, 67, 87]
35
vs22
→ 交换 →[33, 22, 35, 67, 87]
- 结果:第三大元素
35
浮到了倒数第三位置
第 4 回合(i=3):
- 比较次数:1 次
- 过程:
33
vs22
→ 交换 →[22, 33, 35, 67, 87]
- 最终结果:
[22, 33, 35, 67, 87]
3. 优化版代码实现(江湖秘籍)
#include <stdio.h>
void main() {
int a[5] = {87, 35, 67, 33, 22}, j, i, t, k;
// 江湖比武大会
for (i = 0; i < 5; i++) {
k = 0; // 记录本轮是否有侠客位置变动
// 相邻侠客两两比试
for (j = 0; j < 5 - i - 1; j++) {
if (a[j] > a[j + 1]) {
k = 1; // 标记有位置变动
t = a[j];
a[j] = a[j + 1];
a[j + 1] = t; // 胜者晋级,败者后退
}
}
if (!k) break; // 如果本轮无人变动,说明江湖已安定,提前结束
}
// 展示武林排行榜
for (i = 0; i < 5; i++)
printf("%4d", a[i]);
getch();
}
c
五、三大算法江湖对决
1. 时间复杂度比拼
算法 | 最好情况 | 最坏情况 | 平均情况 |
---|---|---|---|
插入排序 | O(n) | O(n²) | O(n²) |
选择排序 | O(n²) | O(n²) | O(n²) |
冒泡排序 | O(n) | O(n²) | O(n²) |
江湖点评:插入排序和冒泡排序在数据基本有序时有惊喜表现,而选择排序则始终如一的 "稳定"(慢)。
2. 空间复杂度比拼
三大算法均为 O (1),都是 "轻量级选手",无需额外空间,适合在内存有限的江湖环境中使用。
3. 稳定性对决
- 插入排序:稳定(相同元素不交换)
- 选择排序:不稳定(可能改变相同元素顺序)
- 冒泡排序:稳定(相同元素不交换)
江湖点评:在需要保持原有顺序的场景中,选择排序会被直接淘汰。
六、江湖实战:如何选择合适的算法?
- 数据规模较小:三种算法均可,但插入排序通常表现更好。
- 数据基本有序:插入排序和优化后的冒泡排序是首选。
- 交换成本较高:选择排序(交换次数最少)。
- 需要稳定性:插入排序或冒泡排序。
江湖口诀:
插入排序像玩牌,步步为营最实在;
选择排序挑精英,精准选择效率平;
冒泡排序比相邻,气泡上浮见真章;
数据有序插冒强,数据乱序另寻良方。
七,总结
通过学习这三种基础排序算法,你不仅掌握了排序的基本原理,还理解了算法设计的核心思想。这些知识是进一步学习高级排序算法(如快速排序、归并排序)的重要基础。建议你亲自编写代码,调试运行,并尝试不同的输入数据观察算法表现。 或者在评论区交流学习,这将帮助你更深入地理解排序算法的本质。