排序算法入门:从桶排序到冒泡排序详解
排序算法入门:从桶排序到冒泡排序
一、文章前言
上一篇文章我们讲解了桶排序,有经验的开发者都知道:桶排序在排序数值较大时,会占用大量数组空间,内存消耗极高。
针对大数值排序场景,我们就需要使用冒泡排序。
二、冒泡排序介绍
一句话总结
像气泡往上冒一样,反复两两比较相邻数字,大的往后挪,一轮轮把最大的数 "浮" 到末尾,直到全部排好。
通俗解释
- 从头开始,相邻两个数字对比大小
- 若前面的数大于后面的数,交换两者位置
- 一轮遍历完成,最大的数会被移动到末尾
- 重复上述步骤,每次无需再对比最后已排好的数
- 直到没有数字需要交换,排序完成
小例子
原数列:3 1 5 2
- 第一轮:1和3交换→3和5不换→5和2交换 → 结果:
1 3 2 5(5 归位) - 第二轮:1和3不换→3和2交换 → 结果:
1 2 3 5(3 归位) - 第三轮:无交换 → 排序完成
三、Zig 语言实现冒泡排序
前言
本次使用 Zig 编程语言实现冒泡排序。Zig 对内存管理机制要求严格,数组元素推荐使用无符号整型;对比桶排序中频繁使用 @intCast 强制类型转换,本次直接使用 u32 类型,简化代码。
完整代码
C
// 冒泡排序 - Zig 实现
const std = @import("std");
const print = std.debug.print;
const c = @cImport({
@cInclude("stdio.h");
});
pub fn main() void {
var a: [100]u32 = undefined;
var n: u32 = 0;
var t: u32 = 0;
// 输入数据
_ = c.scanf("%u", &n);
for (1..n + 1) |i| {
_ = c.scanf("%u", &a[i]);
}
// 冒泡排序核心逻辑
for (0..n - 1) |i| {
for (1..n - i) |j| {
if (a[j] < a[j + 1]) {
// 交换元素
t = a[j];
a[j] = a[j + 1];
a[j + 1] = t;
}
}
}
// 输出结果
for (1..n + 1) |i| {
print("{} ", .{a[i]});
}
}
// Zarek
测试示例
-
输入:
5
12 35 76 88 98 -
输出:
98 88 76 35 12
核心知识点总结
-
循环语法对比
- Zig:
for (0..n + 1) |i| - C语言:
for(int i=0;i<=n;i++) - 两者逻辑完全等价:
0..n为左闭右开区间,n+1可包含目标数值;|i|作用是将循环当前值赋值给变量i。
- Zig:
-
数据类型 :
%u对应无符号整型,匹配 Zig 的u32类型。 -
输入函数 :通过
@cInclude引入 C 标准库,使用c.scanf()实现输入。
四、C3 语言实现冒泡排序
前言
C3 语言 90% 语法与 C 语言一致,原生库未提供替代 scanf 的输入函数,因此直接引入 C 语言的输入函数使用。
完整代码
C
import std;
// 引入 C 语言 scanf 函数
extern fn int scanf(char*, ...);
fn void main()
{
int[100] a;
int i, j, t, n;
// 输入数据
scanf("%d", &n);
for(i=1; i<=n; i++)
{
scanf("%d", &a[i]);
}
// 冒泡排序核心逻辑
for(i=1; i<=n-1; i++)
{
for(j=1; j<=n-1; j++)
{
if(a[j] < a[j+1])
{
t = a[j];
a[j] = a[j+1];
a[j+1] = t;
}
}
}
// 输出结果
for(i=1; i<=n; i++)
{
io::printf("%d ", a[i]);
}
}
语法说明
- 数组定义、循环逻辑与 C 语言高度相似
- 输出函数:C3 使用
io::printf(),区别于 C 语言的printf() - 外部函数:通过
extern声明引入 C 语言scanf
五、全文总结
- 冒泡排序核心:相邻比较,大值后移,适合大数值排序,解决桶排序内存占用过高的问题。
- Zig 语法严谨,内存管理优秀,是三者中语法复杂度最高的语言。
- C3 兼容 C 生态,语法简洁,可直接调用 C 语言标准库函数。
- 多语言同步编写易混淆语法,核心排序逻辑在三种语言中保持一致。